msm: acpuclock-krait: Detect and preserve CPU and L2 clock rates at boot
Previously, the acpuclock-krait driver changed the rates of these clocks
at boot to match the highest performance levels in acpu_freq_tbl. Instead,
detect and preserve the bootloader configuration until acpuclk_set_rate()
is called. This gets acpuclock-krait out of the business of deciding what
rates the CPUs should be running at, and leaves such policy decisions to
the drivers which call it.
Change-Id: I4feba569c471ca3b08cb5201ab65436503b4455e
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
diff --git a/arch/arm/mach-msm/acpuclock-krait.c b/arch/arm/mach-msm/acpuclock-krait.c
index fd43f57..01f6c33 100644
--- a/arch/arm/mach-msm/acpuclock-krait.c
+++ b/arch/arm/mach-msm/acpuclock-krait.c
@@ -54,7 +54,6 @@
static struct drv_data {
struct acpu_level *acpu_freq_tbl;
- const struct acpu_level *max_acpu_lvl;
const struct l2_level *l2_freq_tbl;
struct scalable *scalable;
struct hfpll_data *hfpll_data;
@@ -533,19 +532,21 @@
}
/* Voltage regulator initialization. */
-static int __cpuinit regulator_init(struct scalable *sc)
+static int __cpuinit regulator_init(struct scalable *sc,
+ const struct acpu_level *acpu_level)
{
int ret, vdd_mem, vdd_dig, vdd_core;
- vdd_mem = calculate_vdd_mem(drv.max_acpu_lvl);
- vdd_dig = calculate_vdd_dig(drv.max_acpu_lvl);
-
+ vdd_mem = calculate_vdd_mem(acpu_level);
ret = rpm_regulator_init(sc, VREG_MEM, vdd_mem, true);
if (ret)
goto err_mem;
+
+ vdd_dig = calculate_vdd_dig(acpu_level);
ret = rpm_regulator_init(sc, VREG_DIG, vdd_dig, true);
if (ret)
goto err_dig;
+
ret = rpm_regulator_init(sc, VREG_HFPLL_A,
sc->vreg[VREG_HFPLL_A].max_vdd, false);
if (ret)
@@ -564,7 +565,7 @@
sc->vreg[VREG_CORE].name, ret);
goto err_core_get;
}
- vdd_core = calculate_vdd_core(drv.max_acpu_lvl);
+ vdd_core = calculate_vdd_core(acpu_level);
ret = regulator_set_voltage(sc->vreg[VREG_CORE].reg, vdd_core,
sc->vreg[VREG_CORE].max_vdd);
if (ret) {
@@ -647,9 +648,63 @@
return 0;
}
+static void __cpuinit fill_cur_core_speed(struct core_speed *s,
+ struct scalable *sc)
+{
+ s->pri_src_sel = get_l2_indirect_reg(sc->l2cpmr_iaddr) & 0x3;
+ s->sec_src_sel = (get_l2_indirect_reg(sc->l2cpmr_iaddr) >> 2) & 0x3;
+ s->pll_l_val = readl_relaxed(sc->hfpll_base + drv.hfpll_data->l_offset);
+}
+
+static bool __cpuinit speed_equal(const struct core_speed *s1,
+ const struct core_speed *s2)
+{
+ return (s1->pri_src_sel == s2->pri_src_sel &&
+ s1->sec_src_sel == s2->sec_src_sel &&
+ s1->pll_l_val == s2->pll_l_val);
+}
+
+static const struct acpu_level __cpuinit *find_cur_acpu_level(int cpu)
+{
+ struct scalable *sc = &drv.scalable[cpu];
+ const struct acpu_level *l;
+ struct core_speed cur_speed;
+
+ fill_cur_core_speed(&cur_speed, sc);
+ for (l = drv.acpu_freq_tbl; l->speed.khz != 0; l++)
+ if (speed_equal(&l->speed, &cur_speed))
+ return l;
+ return NULL;
+}
+
+static const struct l2_level __init *find_cur_l2_level(void)
+{
+ struct scalable *sc = &drv.scalable[L2];
+ const struct l2_level *l;
+ struct core_speed cur_speed;
+
+ fill_cur_core_speed(&cur_speed, sc);
+ for (l = drv.l2_freq_tbl; l->speed.khz != 0; l++)
+ if (speed_equal(&l->speed, &cur_speed))
+ return l;
+ return NULL;
+}
+
+static const struct acpu_level __cpuinit *find_min_acpu_level(void)
+{
+ struct acpu_level *l;
+
+ for (l = drv.acpu_freq_tbl; l->speed.khz != 0; l++)
+ if (l->use_for_scaling)
+ return l;
+
+ return NULL;
+}
+
static int __cpuinit per_cpu_init(int cpu)
{
struct scalable *sc = &drv.scalable[cpu];
+ const struct acpu_level *acpu_level;
int ret;
sc->hfpll_base = ioremap(sc->hfpll_phys_base, SZ_32);
@@ -658,14 +713,29 @@
goto err_ioremap;
}
- ret = regulator_init(sc);
+ acpu_level = find_cur_acpu_level(cpu);
+ if (!acpu_level || acpu_level->speed.src == QSB) {
+ acpu_level = find_min_acpu_level();
+ if (!acpu_level) {
+ ret = -ENODEV;
+ goto err_table;
+ }
+ dev_dbg(drv.dev, "CPU%d is running at an unknown rate. Defaulting to %lu KHz.\n",
+ cpu, acpu_level->speed.khz);
+ } else {
+ dev_dbg(drv.dev, "CPU%d is running at %lu KHz\n", cpu,
+ acpu_level->speed.khz);
+ }
+
+ ret = regulator_init(sc, acpu_level);
if (ret)
goto err_regulators;
- ret = init_clock_sources(sc, &drv.max_acpu_lvl->speed);
+ ret = init_clock_sources(sc, &acpu_level->speed);
if (ret)
goto err_clocks;
- sc->l2_vote = drv.max_acpu_lvl->l2_level;
+
+ sc->l2_vote = acpu_level->l2_level;
sc->initialized = true;
return 0;
@@ -673,13 +743,14 @@
err_clocks:
regulator_cleanup(sc);
err_regulators:
+err_table:
iounmap(sc->hfpll_base);
err_ioremap:
return ret;
}
/* Register with bus driver. */
-static void __init bus_init(void)
+static void __init bus_init(const struct l2_level *l2_level)
{
int ret;
@@ -690,7 +761,7 @@
}
ret = msm_bus_scale_client_update_request(drv.bus_perf_client,
- drv.l2_freq_tbl[drv.max_acpu_lvl->l2_level].bw_level);
+ l2_level->bw_level);
if (ret)
dev_err(drv.dev, "initial bandwidth req failed (%d)\n", ret);
}
@@ -840,20 +911,6 @@
return tbl_idx;
}
-static const struct acpu_level __init *find_max_acpu_lvl(struct acpu_level *tbl)
-{
- struct acpu_level *l, *max_lvl = NULL;
-
- for (l = tbl; l->speed.khz != 0; l++)
- if (l->use_for_scaling)
- max_lvl = l;
-
- BUG_ON(!max_lvl);
- dev_info(drv.dev, "Max CPU freq: %lu KHz\n", max_lvl->speed.khz);
-
- return max_lvl;
-}
-
static struct acpuclk_data acpuclk_krait_data = {
.set_rate = acpuclk_krait_set_rate,
.get_rate = acpuclk_krait_get_rate,
@@ -892,20 +949,17 @@
params->pvs_tables[tbl_idx].size,
GFP_KERNEL);
BUG_ON(!drv.acpu_freq_tbl);
-
- drv.max_acpu_lvl = find_max_acpu_lvl(drv.acpu_freq_tbl);
}
static void __init hw_init(void)
{
struct scalable *l2 = &drv.scalable[L2];
+ const struct l2_level *l2_level;
int cpu, rc;
if (krait_needs_vmin())
krait_apply_vmin(drv.acpu_freq_tbl);
- bus_init();
-
l2->hfpll_base = ioremap(l2->hfpll_phys_base, SZ_32);
BUG_ON(!l2->hfpll_base);
@@ -915,14 +969,25 @@
rc = rpm_regulator_init(l2, VREG_HFPLL_B,
l2->vreg[VREG_HFPLL_B].max_vdd, false);
BUG_ON(rc);
- rc = init_clock_sources(l2,
- &drv.l2_freq_tbl[drv.max_acpu_lvl->l2_level].speed);
+
+ l2_level = find_cur_l2_level();
+ if (!l2_level || l2_level->speed.src == QSB) {
+ l2_level = drv.l2_freq_tbl;
+ dev_dbg(drv.dev, "L2 is running at an unknown rate. Defaulting to QSB.\n");
+ } else {
+ dev_dbg(drv.dev, "L2 is running at %lu KHz\n",
+ l2_level->speed.khz);
+ }
+
+ rc = init_clock_sources(l2, &l2_level->speed);
BUG_ON(rc);
for_each_online_cpu(cpu) {
rc = per_cpu_init(cpu);
BUG_ON(rc);
}
+
+ bus_init(l2_level);
}
int __init acpuclk_krait_init(struct device *dev,
diff --git a/arch/arm/mach-msm/acpuclock-krait.h b/arch/arm/mach-msm/acpuclock-krait.h
index f92aaf3..830a0f6 100644
--- a/arch/arm/mach-msm/acpuclock-krait.h
+++ b/arch/arm/mach-msm/acpuclock-krait.h
@@ -116,11 +116,11 @@
* @pll_l_val: HFPLL "L" value to be applied when an HFPLL source is selected.
*/
struct core_speed {
- const unsigned long khz;
- const int src;
- const u32 pri_src_sel;
- const u32 sec_src_sel;
- const u32 pll_l_val;
+ unsigned long khz;
+ int src;
+ u32 pri_src_sel;
+ u32 sec_src_sel;
+ u32 pll_l_val;
};
/**