msm: acpuclock-cortex: Merge common code in 9625 and 8226

Move common code into a new file, and keep target specific data
in the target-specific file, similar to the acpuclock-krait
architecture.

Change-Id: If1d29e09b1dcedd34f439c8ee4cceb744dbc4c68
Signed-off-by: Patrick Daly <pdaly@codeaurora.org>
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 07f5511..7557287 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -296,10 +296,11 @@
 obj-$(CONFIG_ARCH_MSM8610) += gdsc.o
 obj-$(CONFIG_ARCH_MSM8974) += krait-regulator.o
 obj-$(CONFIG_ARCH_MSM9625) += board-9625.o board-9625-gpiomux.o
-obj-$(CONFIG_ARCH_MSM9625) += clock-local2.o clock-pll.o clock-9625.o clock-rpm.o clock-voter.o acpuclock-9625.o
+obj-$(CONFIG_ARCH_MSM9625) += clock-local2.o clock-pll.o clock-9625.o clock-rpm.o clock-voter.o acpuclock-9625.o acpuclock-cortex.o
 obj-$(CONFIG_ARCH_MSM8930) += acpuclock-8930.o acpuclock-8627.o acpuclock-8930aa.o acpuclock-8930ab.o
 obj-$(CONFIG_ARCH_MPQ8092) += board-8092.o board-8092-gpiomux.o
 obj-$(CONFIG_ARCH_MSM8226) += board-8226.o board-8226-gpiomux.o
+obj-$(CONFIG_ARCH_MSM8226) += acpuclock-8226.o acpuclock-cortex.o
 obj-$(CONFIG_ARCH_MSM8610) += board-8610.o board-8610-gpiomux.o
 obj-$(CONFIG_ARCH_MSM8610) += clock-local2.o clock-pll.o clock-8610.o clock-rpm.o clock-voter.o
 
diff --git a/arch/arm/mach-msm/acpuclock-8226.c b/arch/arm/mach-msm/acpuclock-8226.c
new file mode 100644
index 0000000..7dc3a0e
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-8226.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <mach/clk-provider.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/rpm-regulator-smd.h>
+
+#include "acpuclock-cortex.h"
+
+#define RCG_CONFIG_UPDATE_BIT		BIT(0)
+
+static struct msm_bus_paths bw_level_tbl[] = {
+	[0] =  BW_MBPS(152), /* At least 19 MHz on bus. */
+	[1] =  BW_MBPS(300), /* At least 37.5 MHz on bus. */
+	[2] =  BW_MBPS(400), /* At least 50 MHz on bus. */
+	[3] =  BW_MBPS(800), /* At least 100 MHz on bus. */
+	[4] = BW_MBPS(1600), /* At least 200 MHz on bus. */
+	[5] = BW_MBPS(2128), /* At least 266 MHz on bus. */
+	[6] = BW_MBPS(3200), /* At least 400 MHz on bus. */
+	[7] = BW_MBPS(4264), /* At least 533 MHz on bus. */
+};
+
+static struct msm_bus_scale_pdata bus_client_pdata = {
+	.usecase = bw_level_tbl,
+	.num_usecases = ARRAY_SIZE(bw_level_tbl),
+	.active_only = 1,
+	.name = "acpuclock",
+};
+
+/* TODO:
+ * 1) Update MX voltage when data is avaiable
+ * 2) Update bus bandwidth
+ * 3) Depending on Frodo version, may need minimum of LVL_NOM
+ */
+static struct clkctl_acpu_speed acpu_freq_tbl[] = {
+	{ 0,   19200, CXO,     0, 0,   LVL_LOW,    950000, 0 },
+	{ 1,  300000, PLL0,    4, 2,   LVL_LOW,    950000, 4 },
+	{ 1,  384000, ACPUPLL, 5, 0,   LVL_LOW,    950000, 4 },
+	{ 1,  600000, PLL0,    4, 0,   LVL_NOM,    950000, 6 },
+	{ 1,  787200, ACPUPLL, 5, 0,   LVL_NOM,   1050000, 6 },
+	{ 1,  998400, ACPUPLL, 5, 0,   LVL_HIGH,  1050000, 7 },
+	{ 1, 1190400, ACPUPLL, 5, 0,   LVL_HIGH,  1050000, 7 },
+	{ 0 }
+};
+
+static struct acpuclk_drv_data drv_data = {
+	.freq_tbl = acpu_freq_tbl,
+	.current_speed = &(struct clkctl_acpu_speed){ 0 },
+	.bus_scale = &bus_client_pdata,
+	/* FIXME regulator doesn't support corners yet */
+	.vdd_max_cpu = 1050000,
+	.vdd_max_mem = 1150000,
+	.src_clocks = {
+		[PLL0].name = "gpll0",
+		[ACPUPLL].name = "a7sspll",
+	},
+	.reg_data = {
+		.cfg_src_mask = BM(10, 8),
+		.cfg_src_shift = 8,
+		.cfg_div_mask = BM(4, 0),
+		.cfg_div_shift = 0,
+		.update_mask = RCG_CONFIG_UPDATE_BIT,
+		.poll_mask = RCG_CONFIG_UPDATE_BIT,
+	},
+};
+
+static int __init acpuclk_a7_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rcg_base");
+	if (!res)
+		return -EINVAL;
+
+	drv_data.apcs_rcg_cmd = ioremap(res->start, resource_size(res));
+	if (!drv_data.apcs_rcg_cmd)
+		return -ENOMEM;
+
+	drv_data.apcs_rcg_config = drv_data.apcs_rcg_cmd + 4;
+
+	drv_data.vdd_cpu = regulator_get(&pdev->dev, "a7_cpu");
+	if (IS_ERR(drv_data.vdd_cpu)) {
+		dev_err(&pdev->dev, "regulator for %s get failed\n", "a7_cpu");
+		return PTR_ERR(drv_data.vdd_cpu);
+	}
+
+	drv_data.vdd_mem = regulator_get(&pdev->dev, "a7_mem");
+	if (IS_ERR(drv_data.vdd_mem)) {
+		dev_err(&pdev->dev, "regulator for %s get failed\n", "a7_mem");
+		return PTR_ERR(drv_data.vdd_mem);
+	}
+
+	return acpuclk_cortex_init(pdev, &drv_data);
+}
+
+static struct of_device_id acpuclk_a7_match_table[] = {
+	{.compatible = "qcom,acpuclk-a7"},
+	{}
+};
+
+static struct platform_driver acpuclk_a7_driver = {
+	.driver = {
+		.name = "acpuclk-a7",
+		.of_match_table = acpuclk_a7_match_table,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init acpuclk_a7_init(void)
+{
+	return platform_driver_probe(&acpuclk_a7_driver, acpuclk_a7_probe);
+}
+device_initcall(acpuclk_a7_init);
diff --git a/arch/arm/mach-msm/acpuclock-9625.c b/arch/arm/mach-msm/acpuclock-9625.c
index b0556c3..b439088 100644
--- a/arch/arm/mach-msm/acpuclock-9625.c
+++ b/arch/arm/mach-msm/acpuclock-9625.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. 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
@@ -15,96 +15,22 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/mutex.h>
-#include <linux/spinlock.h>
 #include <linux/errno.h>
-#include <linux/cpufreq.h>
-#include <linux/clk.h>
 #include <linux/platform_device.h>
-#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
 
-#include <mach/board.h>
-#include <mach/msm_iomap.h>
 #include <mach/msm_bus.h>
 #include <mach/msm_bus_board.h>
-#include <mach/rpm-regulator.h>
 #include <mach/clk-provider.h>
 #include <mach/rpm-regulator-smd.h>
 
-#include "acpuclock.h"
+#include "acpuclock-cortex.h"
 
-#define RCG_SRC_DIV_MASK		BM(7, 0)
 #define RCG_CONFIG_PGM_DATA_BIT		BIT(11)
 #define RCG_CONFIG_PGM_ENA_BIT		BIT(10)
-#define POLL_INTERVAL_US		1
-#define APCS_RCG_UPDATE_TIMEOUT_US	20
 #define GPLL0_TO_A5_ALWAYS_ENABLE	BIT(18)
 
-#define MAX_VDD_MEM			1050000
-#define MAX_VDD_CPU			1050000
-
-/* Corner type vreg VDD values */
-#define LVL_NONE        RPM_REGULATOR_CORNER_NONE
-#define LVL_LOW         RPM_REGULATOR_CORNER_SVS_SOC
-#define LVL_NOM         RPM_REGULATOR_CORNER_NORMAL
-#define LVL_HIGH        RPM_REGULATOR_CORNER_SUPER_TURBO
-
-enum clk_src {
-	CXO,
-	PLL0,
-	ACPUPLL,
-	NUM_SRC,
-};
-
-struct src_clock {
-	struct clk *clk;
-	const char *name;
-};
-
-static struct src_clock src_clocks[NUM_SRC] = {
-	[PLL0].name = "pll0",
-	[ACPUPLL].name = "pll14",
-};
-
-struct clkctl_acpu_speed {
-	bool use_for_scaling;
-	unsigned int khz;
-	int src;
-	unsigned int src_sel;
-	unsigned int src_div;
-	unsigned int vdd_cpu;
-	unsigned int vdd_mem;
-	unsigned int bw_level;
-};
-
-struct acpuclk_drv_data {
-	struct mutex			lock;
-	struct clkctl_acpu_speed	*current_speed;
-	void __iomem			*apcs_rcg_config;
-	void __iomem			*apcs_cpu_pwr_ctl;
-	struct regulator		*vdd_cpu;
-	struct regulator		*vdd_mem;
-};
-
-static struct acpuclk_drv_data drv_data = {
-	.current_speed = &(struct clkctl_acpu_speed){ 0 },
-};
-
-/* Instantaneous bandwidth requests in MB/s. */
-#define BW_MBPS(_bw) \
-	{ \
-		.vectors = &(struct msm_bus_vectors){ \
-			.src = MSM_BUS_MASTER_AMPSS_M0, \
-			.dst = MSM_BUS_SLAVE_EBI_CH0, \
-			.ib = (_bw) * 1000000UL, \
-			.ab = 0, \
-		}, \
-		.num_paths = 1, \
-	}
-
 static struct msm_bus_paths bw_level_tbl[] = {
 	[0] =  BW_MBPS(152), /* At least 19 MHz on bus. */
 	[1] =  BW_MBPS(264), /* At least 33 MHz on bus. */
@@ -123,8 +49,6 @@
 	.name = "acpuclock",
 };
 
-static uint32_t bus_perf_client;
-
 /* TODO:
  * 1) Update MX voltage when they are avaiable
  * 2) Update bus bandwidth
@@ -138,265 +62,31 @@
 	{ 0 }
 };
 
-/* Update the bus bandwidth request. */
-static void set_bus_bw(unsigned int bw)
-{
-	int ret;
-
-	if (bw >= ARRAY_SIZE(bw_level_tbl)) {
-		pr_err("invalid bandwidth request (%d)\n", bw);
-		return;
-	}
-
-	/* Update bandwidth if request has changed. This may sleep. */
-	ret = msm_bus_scale_client_update_request(bus_perf_client, bw);
-	if (ret)
-		pr_err("bandwidth request failed (%d)\n", ret);
-
-	return;
-}
-
-/* Apply any per-cpu voltage increases. */
-static int increase_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
-{
-	int rc = 0;
-
-	/* Increase vdd_mem before vdd_cpu. vdd_mem should be >= vdd_cpu. */
-	rc = regulator_set_voltage(drv_data.vdd_mem, vdd_mem, MAX_VDD_MEM);
-	if (rc) {
-		pr_err("vdd_mem increase failed (%d)\n", rc);
-		return rc;
-	}
-
-	rc = regulator_set_voltage(drv_data.vdd_cpu, vdd_cpu, MAX_VDD_CPU);
-	if (rc)
-		pr_err("vdd_cpu increase failed (%d)\n", rc);
-
-	return rc;
-}
-
-/* Apply any per-cpu voltage decreases. */
-static void decrease_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
-{
-	int ret;
-
-	/* Update CPU voltage. */
-	ret = regulator_set_voltage(drv_data.vdd_cpu, vdd_cpu, MAX_VDD_CPU);
-	if (ret) {
-		pr_err("vdd_cpu decrease failed (%d)\n", ret);
-		return;
-	}
-
-	/* Decrease vdd_mem after vdd_cpu. vdd_mem should be >= vdd_cpu. */
-	ret = regulator_set_voltage(drv_data.vdd_mem, vdd_mem, MAX_VDD_MEM);
-	if (ret)
-		pr_err("vdd_mem decrease failed (%d)\n", ret);
-}
-
-static void select_clk_source_div(struct clkctl_acpu_speed *s)
-{
-	u32 regval, rc, src_div;
-	void __iomem *apcs_rcg_config = drv_data.apcs_rcg_config;
-
-	src_div = s->src_div ? ((2 * s->src_div) - 1) : s->src_div;
-
-	regval = readl_relaxed(apcs_rcg_config);
-	regval &= ~RCG_SRC_DIV_MASK;
-	regval |= BVAL(2, 0, s->src_sel) | BVAL(7, 3, src_div);
-	writel_relaxed(regval, apcs_rcg_config);
-
-	/*
-	 * Make sure writing of src and div finishes before update
-	 * the configuration
-	 */
-	mb();
-
-	/* Update the configruation */
-	regval = readl_relaxed(apcs_rcg_config);
-	regval |= RCG_CONFIG_PGM_DATA_BIT | RCG_CONFIG_PGM_ENA_BIT;
-	writel_relaxed(regval, apcs_rcg_config);
-
-	/* Wait for update to take effect */
-	rc = readl_poll_timeout(apcs_rcg_config, regval,
-		   !(regval & RCG_CONFIG_PGM_DATA_BIT),
-		   POLL_INTERVAL_US,
-		   APCS_RCG_UPDATE_TIMEOUT_US);
-	if (rc)
-		pr_warn("acpu rcg didn't update its configuration\n");
-}
-
-static int set_speed(struct clkctl_acpu_speed *tgt_s)
-{
-	int rc = 0;
-	unsigned int tgt_freq_hz = tgt_s->khz * 1000;
-	struct clkctl_acpu_speed *strt_s = drv_data.current_speed;
-	struct clkctl_acpu_speed *cxo_s = &acpu_freq_tbl[0];
-	struct clk *strt = src_clocks[strt_s->src].clk;
-	struct clk *tgt = src_clocks[tgt_s->src].clk;
-
-	if (strt_s->src == ACPUPLL && tgt_s->src == ACPUPLL) {
-		/* Switch to another always on src */
-		select_clk_source_div(cxo_s);
-
-		/* Re-program acpu pll */
-		clk_disable(tgt);
-		rc = clk_set_rate(tgt, tgt_freq_hz);
-		if (rc)
-			pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
-		BUG_ON(clk_enable(tgt));
-
-		/* Switch back to acpu pll */
-		select_clk_source_div(tgt_s);
-	} else if (strt_s->src != ACPUPLL && tgt_s->src == ACPUPLL) {
-		rc = clk_set_rate(tgt, tgt_freq_hz);
-		if (rc) {
-			pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
-			return rc;
-		}
-
-		rc = clk_enable(tgt);
-		if (rc) {
-			pr_err("ACPU PLL enable failed\n");
-			return rc;
-		}
-
-		select_clk_source_div(tgt_s);
-
-		clk_disable(strt);
-	} else {
-		rc = clk_enable(tgt);
-		if (rc) {
-			pr_err("%s enable failed\n",
-					src_clocks[tgt_s->src].name);
-			return rc;
-		}
-
-		select_clk_source_div(tgt_s);
-
-		clk_disable(strt);
-	}
-
-	return rc;
-}
-
-static int acpuclk_9625_set_rate(int cpu, unsigned long rate,
-				 enum setrate_reason reason)
-{
-	struct clkctl_acpu_speed *tgt_s, *strt_s;
-	int rc = 0;
-
-	if (reason == SETRATE_CPUFREQ)
-		mutex_lock(&drv_data.lock);
-
-	strt_s = drv_data.current_speed;
-
-	/* Return early if rate didn't change */
-	if (rate == strt_s->khz)
-		goto out;
-
-	/* Find target frequency */
-	for (tgt_s = acpu_freq_tbl; tgt_s->khz != 0; tgt_s++)
-		if (tgt_s->khz == rate)
-			break;
-	if (tgt_s->khz == 0) {
-		rc = -EINVAL;
-		goto out;
-	}
-
-	/* Increase VDD levels if needed */
-	if ((reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
-			&& (tgt_s->khz > strt_s->khz)) {
-		rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
-		if (rc)
-			goto out;
-	}
-
-	pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
-		strt_s->khz, tgt_s->khz);
-
-	/* Switch CPU speed. */
-	rc = set_speed(tgt_s);
-	if (rc)
-		goto out;
-
-	drv_data.current_speed = tgt_s;
-	pr_debug("CPU speed change complete\n");
-
-	/* Nothing else to do for SWFI or power-collapse. */
-	if (reason == SETRATE_SWFI || reason == SETRATE_PC)
-		goto out;
-
-	/* Update bus bandwith request */
-	set_bus_bw(tgt_s->bw_level);
-
-	/* Drop VDD levels if we can. */
-	if (tgt_s->khz < strt_s->khz)
-		decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
-
-out:
-	if (reason == SETRATE_CPUFREQ)
-		mutex_unlock(&drv_data.lock);
-	return rc;
-}
-
-static unsigned long acpuclk_9625_get_rate(int cpu)
-{
-	return drv_data.current_speed->khz;
-}
-
-#ifdef CONFIG_CPU_FREQ_MSM
-static struct cpufreq_frequency_table freq_table[30];
-
-static void __init cpufreq_table_init(void)
-{
-	int i, freq_cnt = 0;
-
-	/* Construct the freq_table tables from acpu_freq_tbl. */
-	for (i = 0; acpu_freq_tbl[i].khz != 0
-			&& freq_cnt < ARRAY_SIZE(freq_table); i++) {
-		if (!acpu_freq_tbl[i].use_for_scaling)
-			continue;
-		freq_table[freq_cnt].index = freq_cnt;
-		freq_table[freq_cnt].frequency = acpu_freq_tbl[i].khz;
-		freq_cnt++;
-	}
-	/* freq_table not big enough to store all usable freqs. */
-	BUG_ON(acpu_freq_tbl[i].khz != 0);
-
-	freq_table[freq_cnt].index = freq_cnt;
-	freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
-
-	pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt);
-
-	/* Register table with CPUFreq. */
-	cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
-}
-#else
-static void __init cpufreq_table_init(void) {}
-#endif
-
-static struct acpuclk_data acpuclk_9625_data = {
-	.set_rate = acpuclk_9625_set_rate,
-	.get_rate = acpuclk_9625_get_rate,
-	.power_collapse_khz = 19200,
-	.wait_for_irq_khz = 19200,
+static struct acpuclk_drv_data drv_data = {
+	.freq_tbl = acpu_freq_tbl,
+	.current_speed = &(struct clkctl_acpu_speed){ 0 },
+	.bus_scale = &bus_client_pdata,
+	.vdd_max_cpu = LVL_HIGH,
+	.vdd_max_mem = 1050000,
+	.src_clocks = {
+		[PLL0].name = "pll0",
+		[ACPUPLL].name = "pll14",
+	},
+	.reg_data = {
+		.cfg_src_mask = BM(2, 0),
+		.cfg_src_shift = 0,
+		.cfg_div_mask = BM(7, 3),
+		.cfg_div_shift = 3,
+		.update_mask = RCG_CONFIG_PGM_DATA_BIT | RCG_CONFIG_PGM_ENA_BIT,
+		.poll_mask = RCG_CONFIG_PGM_DATA_BIT,
+	},
 };
 
 static int __init acpuclk_9625_probe(struct platform_device *pdev)
 {
-	unsigned long max_cpu_khz = 0;
 	struct resource *res;
-	int i, rc;
 	u32 regval;
 
-	mutex_init(&drv_data.lock);
-
-	bus_perf_client = msm_bus_scale_register_client(&bus_client_pdata);
-	if (!bus_perf_client) {
-		pr_err("Unable to register bus client\n");
-		BUG();
-	}
-
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rcg_base");
 	if (!res)
 		return -EINVAL;
@@ -405,6 +95,8 @@
 	if (!drv_data.apcs_rcg_config)
 		return -ENOMEM;
 
+	drv_data.apcs_rcg_cmd = drv_data.apcs_rcg_config;
+
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwr_base");
 	if (!res)
 		return -EINVAL;
@@ -430,60 +122,7 @@
 	regval |= GPLL0_TO_A5_ALWAYS_ENABLE;
 	writel_relaxed(regval, drv_data.apcs_cpu_pwr_ctl);
 
-	for (i = 0; i < NUM_SRC; i++) {
-		if (!src_clocks[i].name)
-			continue;
-		src_clocks[i].clk = clk_get(&pdev->dev, src_clocks[i].name);
-		BUG_ON(IS_ERR(src_clocks[i].clk));
-		/*
-		 * Prepare the PLLs because we enable/disable them
-		 * in atomic context during power collapse/restore.
-		 */
-		BUG_ON(clk_prepare(src_clocks[i].clk));
-	}
-
-	/* Improve boot time by ramping up CPU immediately */
-	for (i = 0; acpu_freq_tbl[i].khz != 0 &&
-				acpu_freq_tbl[i].use_for_scaling; i++)
-		max_cpu_khz = acpu_freq_tbl[i].khz;
-
-	/* Initialize regulators */
-	rc = increase_vdd(acpu_freq_tbl[i].vdd_cpu, acpu_freq_tbl[i].vdd_mem);
-	if (rc)
-		goto err_vdd;
-
-	rc = regulator_enable(drv_data.vdd_mem);
-	if (rc) {
-		dev_err(&pdev->dev, "regulator_enable for a5_mem failed\n");
-		goto err_vdd;
-	}
-
-	rc = regulator_enable(drv_data.vdd_cpu);
-	if (rc) {
-		dev_err(&pdev->dev, "regulator_enable for a5_cpu failed\n");
-		goto err_vdd_cpu;
-	}
-
-	acpuclk_9625_set_rate(smp_processor_id(), max_cpu_khz, SETRATE_INIT);
-
-	acpuclk_register(&acpuclk_9625_data);
-	cpufreq_table_init();
-
-	return 0;
-
-err_vdd_cpu:
-	regulator_disable(drv_data.vdd_mem);
-err_vdd:
-	regulator_put(drv_data.vdd_mem);
-	regulator_put(drv_data.vdd_cpu);
-
-	for (i = 0; i < NUM_SRC; i++) {
-		if (!src_clocks[i].name)
-			continue;
-		clk_unprepare(src_clocks[i].clk);
-		clk_put(src_clocks[i].clk);
-	}
-	return rc;
+	return acpuclk_cortex_init(pdev, &drv_data);
 }
 
 static struct of_device_id acpuclk_9625_match_table[] = {
diff --git a/arch/arm/mach-msm/acpuclock-cortex.c b/arch/arm/mach-msm/acpuclock-cortex.c
new file mode 100644
index 0000000..64e31ba
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-cortex.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/iopoll.h>
+
+#include <mach/board.h>
+#include <mach/msm_iomap.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
+#include <mach/rpm-regulator.h>
+#include <mach/clk-provider.h>
+#include <mach/rpm-regulator-smd.h>
+
+#include "acpuclock.h"
+#include "acpuclock-cortex.h"
+
+#define POLL_INTERVAL_US		1
+#define APCS_RCG_UPDATE_TIMEOUT_US	20
+
+static struct acpuclk_drv_data *acpuclk_init_data;
+static uint32_t bus_perf_client;
+
+/* Update the bus bandwidth request. */
+static void set_bus_bw(unsigned int bw)
+{
+	int ret;
+
+	if (bw >= acpuclk_init_data->bus_scale->num_usecases) {
+		pr_err("invalid bandwidth request (%d)\n", bw);
+		return;
+	}
+
+	/* Update bandwidth if request has changed. This may sleep. */
+	ret = msm_bus_scale_client_update_request(bus_perf_client, bw);
+	if (ret)
+		pr_err("bandwidth request failed (%d)\n", ret);
+
+	return;
+}
+
+/* Apply any voltage increases. */
+static int increase_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
+{
+	int rc = 0;
+
+	/* Increase vdd_mem before vdd_cpu. vdd_mem should be >= vdd_cpu. */
+	rc = regulator_set_voltage(acpuclk_init_data->vdd_mem, vdd_mem,
+		acpuclk_init_data->vdd_max_mem);
+	if (rc) {
+		pr_err("vdd_mem increase failed (%d)\n", rc);
+		return rc;
+	}
+
+	rc = regulator_set_voltage(acpuclk_init_data->vdd_cpu, vdd_cpu,
+		acpuclk_init_data->vdd_max_cpu);
+	if (rc)
+		pr_err("vdd_cpu increase failed (%d)\n", rc);
+
+	return rc;
+}
+
+/* Apply any per-cpu voltage decreases. */
+static void decrease_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
+{
+	int ret;
+
+	/* Update CPU voltage. */
+	ret = regulator_set_voltage(acpuclk_init_data->vdd_cpu, vdd_cpu,
+		acpuclk_init_data->vdd_max_cpu);
+	if (ret) {
+		pr_err("vdd_cpu decrease failed (%d)\n", ret);
+		return;
+	}
+
+	/* Decrease vdd_mem after vdd_cpu. vdd_mem should be >= vdd_cpu. */
+	ret = regulator_set_voltage(acpuclk_init_data->vdd_mem, vdd_mem,
+		acpuclk_init_data->vdd_max_mem);
+	if (ret)
+		pr_err("vdd_mem decrease failed (%d)\n", ret);
+}
+
+static void select_clk_source_div(struct acpuclk_drv_data *drv_data,
+	struct clkctl_acpu_speed *s)
+{
+	u32 regval, rc, src_div;
+	void __iomem *apcs_rcg_config = drv_data->apcs_rcg_config;
+	void __iomem *apcs_rcg_cmd = drv_data->apcs_rcg_cmd;
+	struct acpuclk_reg_data *r = &drv_data->reg_data;
+
+	src_div = s->src_div ? ((2 * s->src_div) - 1) : s->src_div;
+
+	regval = readl_relaxed(apcs_rcg_config);
+	regval &= ~r->cfg_src_mask;
+	regval |= s->src_sel << r->cfg_src_shift;
+	regval &= ~r->cfg_div_mask;
+	regval |= src_div << r->cfg_div_shift;
+	writel_relaxed(regval, apcs_rcg_config);
+
+	/* Update the configuration */
+	regval = readl_relaxed(apcs_rcg_cmd);
+	regval |= r->update_mask;
+	writel_relaxed(regval, apcs_rcg_cmd);
+
+	/* Wait for the update to take effect */
+	rc = readl_poll_timeout(apcs_rcg_cmd, regval,
+		   !(regval & r->poll_mask),
+		   POLL_INTERVAL_US,
+		   APCS_RCG_UPDATE_TIMEOUT_US);
+	if (rc)
+		pr_warn("acpu rcg didn't update its configuration\n");
+}
+
+static int set_speed(struct clkctl_acpu_speed *tgt_s)
+{
+	int rc = 0;
+	unsigned int tgt_freq_hz = tgt_s->khz * 1000;
+	struct clkctl_acpu_speed *strt_s = acpuclk_init_data->current_speed;
+	struct clkctl_acpu_speed *cxo_s = &acpuclk_init_data->freq_tbl[0];
+	struct clk *strt = acpuclk_init_data->src_clocks[strt_s->src].clk;
+	struct clk *tgt = acpuclk_init_data->src_clocks[tgt_s->src].clk;
+
+	if (strt_s->src == ACPUPLL && tgt_s->src == ACPUPLL) {
+		/* Switch to another always on src */
+		select_clk_source_div(acpuclk_init_data, cxo_s);
+
+		/* Re-program acpu pll */
+		clk_disable(tgt);
+		rc = clk_set_rate(tgt, tgt_freq_hz);
+		if (rc)
+			pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
+		BUG_ON(clk_enable(tgt));
+
+		/* Switch back to acpu pll */
+		select_clk_source_div(acpuclk_init_data, tgt_s);
+
+	} else if (strt_s->src != ACPUPLL && tgt_s->src == ACPUPLL) {
+		rc = clk_set_rate(tgt, tgt_freq_hz);
+		if (rc) {
+			pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
+			return rc;
+		}
+
+		rc = clk_enable(tgt);
+		if (rc) {
+			pr_err("ACPU PLL enable failed\n");
+			return rc;
+		}
+
+		select_clk_source_div(acpuclk_init_data, tgt_s);
+
+		clk_disable(strt);
+	} else {
+		rc = clk_enable(tgt);
+		if (rc) {
+			pr_err("%s enable failed\n",
+				acpuclk_init_data->src_clocks[tgt_s->src].name);
+			return rc;
+		}
+
+		select_clk_source_div(acpuclk_init_data, tgt_s);
+
+		clk_disable(strt);
+	}
+
+	return rc;
+}
+
+static int acpuclk_cortex_set_rate(int cpu, unsigned long rate,
+				 enum setrate_reason reason)
+{
+	struct clkctl_acpu_speed *tgt_s, *strt_s;
+	int rc = 0;
+
+	if (reason == SETRATE_CPUFREQ)
+		mutex_lock(&acpuclk_init_data->lock);
+
+	strt_s = acpuclk_init_data->current_speed;
+
+	/* Return early if rate didn't change */
+	if (rate == strt_s->khz)
+		goto out;
+
+	/* Find target frequency */
+	for (tgt_s = acpuclk_init_data->freq_tbl; tgt_s->khz != 0; tgt_s++)
+		if (tgt_s->khz == rate)
+			break;
+	if (tgt_s->khz == 0) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* Increase VDD levels if needed */
+	if ((reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
+			&& (tgt_s->khz > strt_s->khz)) {
+		rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
+		if (rc)
+			goto out;
+	}
+
+	pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
+		strt_s->khz, tgt_s->khz);
+
+	/* Switch CPU speed. */
+	rc = set_speed(tgt_s);
+	if (rc)
+		goto out;
+
+	acpuclk_init_data->current_speed = tgt_s;
+	pr_debug("CPU speed change complete\n");
+
+	/* Nothing else to do for SWFI or power-collapse. */
+	if (reason == SETRATE_SWFI || reason == SETRATE_PC)
+		goto out;
+
+	/* Update bus bandwith request */
+	set_bus_bw(tgt_s->bw_level);
+
+	/* Drop VDD levels if we can. */
+	if (tgt_s->khz < strt_s->khz)
+		decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
+
+out:
+	if (reason == SETRATE_CPUFREQ)
+		mutex_unlock(&acpuclk_init_data->lock);
+	return rc;
+}
+
+static unsigned long acpuclk_cortex_get_rate(int cpu)
+{
+	return acpuclk_init_data->current_speed->khz;
+}
+
+#ifdef CONFIG_CPU_FREQ_MSM
+static struct cpufreq_frequency_table freq_table[30];
+
+static void __init cpufreq_table_init(void)
+{
+	int i, freq_cnt = 0;
+
+	/* Construct the freq_table tables from acpuclk_init_data->freq_tbl. */
+	for (i = 0; acpuclk_init_data->freq_tbl[i].khz != 0
+			&& freq_cnt < ARRAY_SIZE(freq_table); i++) {
+		if (!acpuclk_init_data->freq_tbl[i].use_for_scaling)
+			continue;
+		freq_table[freq_cnt].index = freq_cnt;
+		freq_table[freq_cnt].frequency =
+			acpuclk_init_data->freq_tbl[i].khz;
+		freq_cnt++;
+	}
+	/* freq_table not big enough to store all usable freqs. */
+	BUG_ON(acpuclk_init_data->freq_tbl[i].khz != 0);
+
+	freq_table[freq_cnt].index = freq_cnt;
+	freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
+
+	pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt);
+
+	/* Register table with CPUFreq. */
+	for_each_possible_cpu(i)
+		cpufreq_frequency_table_get_attr(freq_table, i);
+}
+#else
+static void __init cpufreq_table_init(void) {}
+#endif
+
+static struct acpuclk_data acpuclk_cortex_data = {
+	.set_rate = acpuclk_cortex_set_rate,
+	.get_rate = acpuclk_cortex_get_rate,
+	.power_collapse_khz = 19200,
+	.wait_for_irq_khz = 19200,
+};
+
+int __init acpuclk_cortex_init(struct platform_device *pdev,
+	struct acpuclk_drv_data *data)
+{
+	unsigned long max_cpu_khz = 0;
+	int i, rc;
+
+	acpuclk_init_data = data;
+	mutex_init(&acpuclk_init_data->lock);
+
+	bus_perf_client = msm_bus_scale_register_client(
+		acpuclk_init_data->bus_scale);
+	if (!bus_perf_client) {
+		pr_err("Unable to register bus client\n");
+		BUG();
+	}
+
+	for (i = 0; i < NUM_SRC; i++) {
+		if (!acpuclk_init_data->src_clocks[i].name)
+			continue;
+		acpuclk_init_data->src_clocks[i].clk =
+			clk_get(&pdev->dev,
+				acpuclk_init_data->src_clocks[i].name);
+		BUG_ON(IS_ERR(acpuclk_init_data->src_clocks[i].clk));
+		/*
+		 * Prepare the PLLs because we enable/disable them
+		 * in atomic context during power collapse/restore.
+		 */
+		BUG_ON(clk_prepare(acpuclk_init_data->src_clocks[i].clk));
+	}
+
+	/* Improve boot time by ramping up CPU immediately */
+	for (i = 0; acpuclk_init_data->freq_tbl[i].khz != 0 &&
+			acpuclk_init_data->freq_tbl[i].use_for_scaling; i++)
+		max_cpu_khz = acpuclk_init_data->freq_tbl[i].khz;
+
+	/* Initialize regulators */
+	rc = increase_vdd(acpuclk_init_data->freq_tbl[i].vdd_cpu,
+		acpuclk_init_data->freq_tbl[i].vdd_mem);
+	if (rc)
+		goto err_vdd;
+
+	rc = regulator_enable(acpuclk_init_data->vdd_mem);
+	if (rc) {
+		dev_err(&pdev->dev, "regulator_enable for mem failed\n");
+		goto err_vdd;
+	}
+
+	rc = regulator_enable(acpuclk_init_data->vdd_cpu);
+	if (rc) {
+		dev_err(&pdev->dev, "regulator_enable for cpu failed\n");
+		goto err_vdd_cpu;
+	}
+
+	acpuclk_cortex_set_rate(0, max_cpu_khz, SETRATE_INIT);
+
+	acpuclk_register(&acpuclk_cortex_data);
+	cpufreq_table_init();
+
+	return 0;
+
+err_vdd_cpu:
+	regulator_disable(acpuclk_init_data->vdd_mem);
+err_vdd:
+	regulator_put(acpuclk_init_data->vdd_mem);
+	regulator_put(acpuclk_init_data->vdd_cpu);
+
+	for (i = 0; i < NUM_SRC; i++) {
+		if (!acpuclk_init_data->src_clocks[i].name)
+			continue;
+		clk_unprepare(acpuclk_init_data->src_clocks[i].clk);
+		clk_put(acpuclk_init_data->src_clocks[i].clk);
+	}
+	return rc;
+}
diff --git a/arch/arm/mach-msm/acpuclock-cortex.h b/arch/arm/mach-msm/acpuclock-cortex.h
new file mode 100644
index 0000000..2db3987
--- /dev/null
+++ b/arch/arm/mach-msm/acpuclock-cortex.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. 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.
+ */
+
+/* Corner type vreg VDD values */
+#define LVL_NONE        RPM_REGULATOR_CORNER_NONE
+#define LVL_LOW         RPM_REGULATOR_CORNER_SVS_SOC
+#define LVL_NOM         RPM_REGULATOR_CORNER_NORMAL
+#define LVL_HIGH        RPM_REGULATOR_CORNER_SUPER_TURBO
+
+enum clk_src {
+	CXO,
+	PLL0,
+	ACPUPLL,
+	NUM_SRC,
+};
+
+struct src_clock {
+	struct clk *clk;
+	const char *name;
+};
+
+struct clkctl_acpu_speed {
+	bool use_for_scaling;
+	unsigned int khz;
+	int src;
+	unsigned int src_sel;
+	unsigned int src_div;
+	unsigned int vdd_cpu;
+	unsigned int vdd_mem;
+	unsigned int bw_level;
+};
+
+struct acpuclk_reg_data {
+	u32 cfg_src_mask;
+	u32 cfg_src_shift;
+	u32 cfg_div_mask;
+	u32 cfg_div_shift;
+	u32 update_mask;
+	u32 poll_mask;
+};
+
+struct acpuclk_drv_data {
+	struct mutex			lock;
+	struct clkctl_acpu_speed	*freq_tbl;
+	struct clkctl_acpu_speed	*current_speed;
+	struct msm_bus_scale_pdata	*bus_scale;
+	void __iomem			*apcs_rcg_config;
+	void __iomem			*apcs_rcg_cmd;
+	void __iomem			*apcs_cpu_pwr_ctl;
+	struct regulator		*vdd_cpu;
+	unsigned long			vdd_max_cpu;
+	struct regulator		*vdd_mem;
+	unsigned long			vdd_max_mem;
+	struct src_clock		src_clocks[NUM_SRC];
+	struct acpuclk_reg_data		reg_data;
+};
+
+/* Instantaneous bandwidth requests in MB/s. */
+#define BW_MBPS(_bw) \
+	{ \
+		.vectors = &(struct msm_bus_vectors){ \
+			.src = MSM_BUS_MASTER_AMPSS_M0, \
+			.dst = MSM_BUS_SLAVE_EBI_CH0, \
+			.ib = (_bw) * 1000000ULL, \
+			.ab = 0, \
+		}, \
+		.num_paths = 1, \
+	}
+
+int __init acpuclk_cortex_init(struct platform_device *pdev,
+	struct acpuclk_drv_data *data);
+