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);
+