msm: acpuclock-7201: Convert to clock APIs for PLL control
Change-Id: I3270f3169e02cadf7e66e51edaa732555b1ff760
Signed-off-by: Pankaj Kumar <pakuma@codeaurora.org>
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 8347a25..f7cc4e3 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -23,7 +23,7 @@
obj-y += acpuclock.o
obj-$(CONFIG_ARCH_MSM7X01A) += acpuclock-7201.o
obj-$(CONFIG_ARCH_MSM7X25) += acpuclock-7201.o
-obj-$(CONFIG_ARCH_MSM7X27) += acpuclock-7201.o
+obj-$(CONFIG_ARCH_MSM7X27) += acpuclock-7201.o clock-pll.o
obj-$(CONFIG_ARCH_MSM_SCORPION) += pmu.o
obj-$(CONFIG_ARCH_MSM_KRAIT) += msm-krait-l2-accessors.o pmu.o
obj-$(CONFIG_ARCH_MSM7X27A) += pmu.o
diff --git a/arch/arm/mach-msm/acpuclock-7201.c b/arch/arm/mach-msm/acpuclock-7201.c
index 5a21407..c4409ff 100644
--- a/arch/arm/mach-msm/acpuclock-7201.c
+++ b/arch/arm/mach-msm/acpuclock-7201.c
@@ -27,7 +27,6 @@
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/sort.h>
-#include <linux/remote_spinlock.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
#include <asm/mach-types.h>
@@ -39,17 +38,16 @@
#define A11S_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100)
#define A11S_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104)
#define A11S_VDD_SVS_PLEVEL_ADDR (MSM_CSR_BASE + 0x124)
-#define PLLn_MODE(n) (MSM_CLK_CTL_BASE + 0x300 + 28 * (n))
-#define PLLn_L_VAL(n) (MSM_CLK_CTL_BASE + 0x304 + 28 * (n))
-#define PLL4_MODE (MSM_CLK_CTL_BASE + 0x374)
-#define PLL4_L_VAL (MSM_CLK_CTL_BASE + 0x378)
#define POWER_COLLAPSE_KHZ 19200
/* Max CPU frequency allowed by hardware while in standby waiting for an irq. */
#define MAX_WAIT_FOR_IRQ_KHZ 128000
+/**
+ * enum - For acpuclock PLL IDs
+ */
enum {
ACPU_PLL_TCXO = -1,
ACPU_PLL_0 = 0,
@@ -60,15 +58,16 @@
ACPU_PLL_END,
};
-static const struct pll {
- void __iomem *mod_reg;
- const uint32_t l_val_mask;
-} soc_pll[ACPU_PLL_END] = {
- [ACPU_PLL_0] = {PLLn_MODE(ACPU_PLL_0), 0x3f},
- [ACPU_PLL_1] = {PLLn_MODE(ACPU_PLL_1), 0x3f},
- [ACPU_PLL_2] = {PLLn_MODE(ACPU_PLL_2), 0x3f},
- [ACPU_PLL_3] = {PLLn_MODE(ACPU_PLL_3), 0x3f},
- [ACPU_PLL_4] = {PLL4_MODE, 0x3ff},
+struct acpu_clk_src {
+ struct clk *clk;
+ const char *name;
+};
+
+static struct acpu_clk_src pll_clk[ACPU_PLL_END] = {
+ [ACPU_PLL_0] = { .name = "pll0_clk" },
+ [ACPU_PLL_1] = { .name = "pll1_clk" },
+ [ACPU_PLL_2] = { .name = "pll2_clk" },
+ [ACPU_PLL_4] = { .name = "pll4_clk" },
};
struct clock_state {
@@ -78,24 +77,6 @@
struct clk *ebi1_clk;
};
-#define PLL_BASE 7
-
-struct shared_pll_control {
- uint32_t version;
- struct {
- /* Denotes if the PLL is ON. Technically, this can be read
- * directly from the PLL registers, but this feild is here,
- * so let's use it.
- */
- uint32_t on;
- /* One bit for each processor core. The application processor
- * is allocated bit position 1. All other bits should be
- * considered as votes from other processors.
- */
- uint32_t votes;
- } pll[PLL_BASE + ACPU_PLL_END];
-};
-
struct clkctl_acpu_speed {
unsigned int use_for_scaling;
unsigned int a11clk_khz;
@@ -112,8 +93,6 @@
struct clkctl_acpu_speed *up[ACPU_PLL_END];
};
-static remote_spinlock_t pll_lock;
-static struct shared_pll_control *pll_control;
static struct clock_state drv_state = { 0 };
static struct clkctl_acpu_speed *acpu_freq_tbl;
@@ -329,26 +308,16 @@
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
};
-#define PLL_0_MHZ 0
-#define PLL_196_MHZ 10
-#define PLL_245_MHZ 12
-#define PLL_589_MHZ 30
-#define PLL_737_MHZ 38
-#define PLL_800_MHZ 41
-#define PLL_960_MHZ 50
-#define PLL_1008_MHZ 52
-#define PLL_1200_MHZ 62
-
#define PLL_CONFIG(m0, m1, m2, m4) { \
- PLL_##m0##_MHZ, PLL_##m1##_MHZ, PLL_##m2##_MHZ, PLL_##m4##_MHZ, \
+ m0, m1, m2, m4, \
pll0_##m0##_pll1_##m1##_pll2_##m2##_pll4_##m4 \
}
struct pll_freq_tbl_map {
- unsigned int pll0_l;
- unsigned int pll1_l;
- unsigned int pll2_l;
- unsigned int pll4_l;
+ unsigned int pll0_rate;
+ unsigned int pll1_rate;
+ unsigned int pll2_rate;
+ unsigned int pll4_rate;
struct clkctl_acpu_speed *tbl;
};
@@ -405,59 +374,6 @@
}
#endif
-static void pll_enable(void __iomem *addr, unsigned on)
-{
- if (on) {
- writel_relaxed(2, addr);
- mb();
- udelay(5);
- writel_relaxed(6, addr);
- mb();
- udelay(50);
- writel_relaxed(7, addr);
- } else {
- writel_relaxed(0, addr);
- }
-}
-
-static int pc_pll_request(unsigned id, unsigned on)
-{
- int res = 0;
- on = !!on;
-
- if (on)
- pr_debug("Enabling PLL %d\n", id);
- else
- pr_debug("Disabling PLL %d\n", id);
-
- if (id >= ACPU_PLL_END)
- return -EINVAL;
-
- remote_spin_lock(&pll_lock);
- if (on) {
- pll_control->pll[PLL_BASE + id].votes |= 2;
- if (!pll_control->pll[PLL_BASE + id].on) {
- pll_enable(soc_pll[id].mod_reg, 1);
- pll_control->pll[PLL_BASE + id].on = 1;
- }
- } else {
- pll_control->pll[PLL_BASE + id].votes &= ~2;
- if (pll_control->pll[PLL_BASE + id].on
- && !pll_control->pll[PLL_BASE + id].votes) {
- pll_enable(soc_pll[id].mod_reg, 0);
- pll_control->pll[PLL_BASE + id].on = 0;
- }
- }
- remote_spin_unlock(&pll_lock);
-
- if (on)
- pr_debug("PLL enabled\n");
- else
- pr_debug("PLL disabled\n");
-
- return res;
-}
-
static int acpuclk_set_vdd_level(int vdd)
{
uint32_t current_vdd;
@@ -576,7 +492,7 @@
if (reason == SETRATE_CPUFREQ) {
if (strt_s->pll != tgt_s->pll && tgt_s->pll != ACPU_PLL_TCXO) {
- rc = pc_pll_request(tgt_s->pll, 1);
+ rc = clk_prepare_enable(pll_clk[tgt_s->pll].clk);
if (rc < 0) {
pr_err("PLL%d enable failed (%d)\n",
tgt_s->pll, rc);
@@ -651,7 +567,7 @@
if (cur_s->pll != ACPU_PLL_TCXO
&& !(plls_enabled & (1 << cur_s->pll))) {
- rc = pc_pll_request(cur_s->pll, 1);
+ rc = clk_prepare_enable(pll_clk[cur_s->pll].clk);
if (rc < 0) {
pr_err("PLL%d enable failed (%d)\n",
cur_s->pll, rc);
@@ -684,12 +600,8 @@
if (tgt_s->pll != ACPU_PLL_TCXO)
plls_enabled &= ~(1 << tgt_s->pll);
for (pll = ACPU_PLL_0; pll < ACPU_PLL_END; pll++)
- if (plls_enabled & (1 << pll)) {
- res = pc_pll_request(pll, 0);
- if (res < 0)
- pr_warning("PLL%d disable failed (%d)\n",
- pll, res);
- }
+ if (plls_enabled & (1 << pll))
+ clk_disable_unprepare(pll_clk[pll].clk);
/* Nothing else to do for power collapse. */
if (reason == SETRATE_PC)
@@ -742,9 +654,10 @@
}
drv_state.current_speed = speed;
- if (speed->pll != ACPU_PLL_TCXO)
- if (pc_pll_request(speed->pll, 1))
+ if (speed->pll != ACPU_PLL_TCXO) {
+ if (clk_prepare_enable(pll_clk[speed->pll].clk))
pr_warning("Failed to vote for boot PLL\n");
+ }
/* Fix div2 to 2 for 7x27/5a(aa) targets */
if (!cpu_is_msm7x27()) {
@@ -777,64 +690,52 @@
/*----------------------------------------------------------------------------
* Clock driver initialization
*---------------------------------------------------------------------------*/
-
-static void __init acpu_freq_tbl_fixup(void)
+#define MHZ 1000000
+static void __init select_freq_plan(void)
{
- unsigned long pll0_l, pll1_l, pll2_l, pll4_l;
- struct pll_freq_tbl_map *lst;
+ unsigned long pll_mhz[ACPU_PLL_END];
+ struct pll_freq_tbl_map *t;
+ int i;
- /* Wait for the PLLs to be initialized and then read their frequency.
- */
- do {
- pll0_l = readl_relaxed(PLLn_L_VAL(0)) &
- soc_pll[ACPU_PLL_0].l_val_mask;
- cpu_relax();
- udelay(50);
- } while (pll0_l == 0);
- do {
- pll1_l = readl_relaxed(PLLn_L_VAL(1)) &
- soc_pll[ACPU_PLL_1].l_val_mask;
- cpu_relax();
- udelay(50);
- } while (pll1_l == 0);
- do {
- pll2_l = readl_relaxed(PLLn_L_VAL(2)) &
- soc_pll[ACPU_PLL_2].l_val_mask;
- cpu_relax();
- udelay(50);
- } while (pll2_l == 0);
-
- pr_info("L val: PLL0: %d, PLL1: %d, PLL2: %d\n",
- (int)pll0_l, (int)pll1_l, (int)pll2_l);
-
- if (!cpu_is_msm7x27() && !cpu_is_msm7x25a()) {
- do {
- pll4_l = readl_relaxed(PLL4_L_VAL) &
- soc_pll[ACPU_PLL_4].l_val_mask;
- cpu_relax();
- udelay(50);
- } while (pll4_l == 0);
- pr_info("L val: PLL4: %d\n", (int)pll4_l);
- } else {
- pll4_l = 0;
+ /* Get PLL clocks */
+ for (i = 0; i < ACPU_PLL_END; i++) {
+ if (pll_clk[i].name) {
+ pll_clk[i].clk = clk_get_sys("acpu", pll_clk[i].name);
+ if (IS_ERR(pll_clk[i].clk)) {
+ pll_mhz[i] = 0;
+ continue;
+ }
+ /* Get PLL's Rate */
+ pll_mhz[i] = clk_get_rate(pll_clk[i].clk)/MHZ;
+ }
}
- /* Fix the tables for 7x25a variant to not conflict with 7x27 ones */
+ /*
+ * For the pll configuration used in acpuclock table e.g.
+ * pll0_960_pll1_245_pll2_1200" is same for 7627 and
+ * 7625a (as pll0,pll1,pll2) having same rates, but frequency
+ * table is different for both targets.
+ *
+ * Hence below for loop will not be able to select correct
+ * table based on PLL rates as rates are same. Hence we need
+ * to add this cpu check for selecting the correct acpuclock table.
+ */
if (cpu_is_msm7x25a()) {
- if (pll1_l == PLL_245_MHZ) {
+ if (pll_mhz[ACPU_PLL_1] == 245) {
acpu_freq_tbl =
pll0_960_pll1_245_pll2_1200_25a;
- } else if (pll1_l == PLL_737_MHZ) {
+ } else if (pll_mhz[ACPU_PLL_1] == 737) {
acpu_freq_tbl =
pll0_960_pll1_737_pll2_1200_25a;
}
} else {
/* Select the right table to use. */
- for (lst = acpu_freq_tbl_list; lst->tbl != 0; lst++) {
- if (lst->pll0_l == pll0_l && lst->pll1_l == pll1_l
- && lst->pll2_l == pll2_l
- && lst->pll4_l == pll4_l) {
- acpu_freq_tbl = lst->tbl;
+ for (t = acpu_freq_tbl_list; t->tbl != 0; t++) {
+ if (t->pll0_rate == pll_mhz[ACPU_PLL_0]
+ && t->pll1_rate == pll_mhz[ACPU_PLL_1]
+ && t->pll2_rate == pll_mhz[ACPU_PLL_2]
+ && t->pll4_rate == pll_mhz[ACPU_PLL_4]) {
+ acpu_freq_tbl = t->tbl;
break;
}
}
@@ -945,33 +846,6 @@
}
}
-static void shared_pll_control_init(void)
-{
-#define PLL_REMOTE_SPINLOCK_ID "S:7"
- unsigned smem_size;
-
- remote_spin_lock_init(&pll_lock, PLL_REMOTE_SPINLOCK_ID);
- pll_control = smem_get_entry(SMEM_CLKREGIM_SOURCES, &smem_size);
-
- if (!pll_control) {
- pr_err("Can't find shared PLL control data structure!\n");
- BUG();
- /* There might be more PLLs than what the application processor knows
- * about. But the index used for each PLL is guaranteed to remain the
- * same. */
- } else if (smem_size < sizeof(struct shared_pll_control)) {
- pr_err("Shared PLL control data"
- "structure too small!\n");
- BUG();
- } else if (pll_control->version != 0xCCEE0001) {
- pr_err("Shared PLL control version mismatch!\n");
- BUG();
- } else {
- pr_info("Shared PLL control available.\n");
- return;
- }
-
-}
static struct acpuclk_data acpuclk_7627_data = {
.set_rate = acpuclk_7627_set_rate,
@@ -988,9 +862,8 @@
BUG_ON(IS_ERR(drv_state.ebi1_clk));
mutex_init(&drv_state.lock);
- shared_pll_control_init();
drv_state.max_speed_delta_khz = soc_data->max_speed_delta_khz;
- acpu_freq_tbl_fixup();
+ select_freq_plan();
acpuclk_7627_data.wait_for_irq_khz = find_wait_for_irq_khz();
precompute_stepping();
acpuclk_hw_init();
diff --git a/arch/arm/mach-msm/clock-pcom-lookup.c b/arch/arm/mach-msm/clock-pcom-lookup.c
index 200cbfe..c20b7e4 100644
--- a/arch/arm/mach-msm/clock-pcom-lookup.c
+++ b/arch/arm/mach-msm/clock-pcom-lookup.c
@@ -11,9 +11,16 @@
*/
#include "clock.h"
+#include "clock-pll.h"
#include "clock-pcom.h"
#include "clock-voter.h"
+#include <mach/msm_iomap.h>
+#include <mach/socinfo.h>
+
+#define PLLn_MODE(n) (MSM_CLK_CTL_BASE + 0x300 + 28 * (n))
+#define PLL4_MODE (MSM_CLK_CTL_BASE + 0x374)
+
static DEFINE_CLK_PCOM(adm_clk, ADM_CLK, CLKFLAG_SKIP_AUTO_OFF);
static DEFINE_CLK_PCOM(adsp_clk, ADSP_CLK, CLKFLAG_SKIP_AUTO_OFF);
static DEFINE_CLK_PCOM(ahb_m_clk, AHB_M_CLK, CLKFLAG_SKIP_AUTO_OFF);
@@ -28,6 +35,46 @@
static DEFINE_CLK_PCOM(csi1_p_clk, CSI1_P_CLK, CLKFLAG_SKIP_AUTO_OFF);
static DEFINE_CLK_PCOM(csi1_vfe_clk, CSI1_VFE_CLK, CLKFLAG_SKIP_AUTO_OFF);
+static struct pll_shared_clk pll0_clk = {
+ .id = PLL_0,
+ .mode_reg = PLLn_MODE(0),
+ .c = {
+ .ops = &clk_pll_ops,
+ .dbg_name = "pll0_clk",
+ CLK_INIT(pll0_clk.c),
+ },
+};
+
+static struct pll_shared_clk pll1_clk = {
+ .id = PLL_1,
+ .mode_reg = PLLn_MODE(1),
+ .c = {
+ .ops = &clk_pll_ops,
+ .dbg_name = "pll1_clk",
+ CLK_INIT(pll1_clk.c),
+ },
+};
+
+static struct pll_shared_clk pll2_clk = {
+ .id = PLL_2,
+ .mode_reg = PLLn_MODE(2),
+ .c = {
+ .ops = &clk_pll_ops,
+ .dbg_name = "pll2_clk",
+ CLK_INIT(pll2_clk.c),
+ },
+};
+
+static struct pll_shared_clk pll4_clk = {
+ .id = PLL_4,
+ .mode_reg = PLL4_MODE,
+ .c = {
+ .ops = &clk_pll_ops,
+ .dbg_name = "pll4_clk",
+ CLK_INIT(pll4_clk.c),
+ },
+};
+
static struct pcom_clk dsi_byte_clk = {
.id = P_DSI_BYTE_CLK,
.c = {
@@ -251,14 +298,20 @@
CLK_LOOKUP("core_clk", ebi_usb_clk.c, "msm_otg"),
CLK_LOOKUP("ebi1_vfe_clk", ebi_vfe_clk.c, NULL),
CLK_LOOKUP("mem_clk", ebi_adm_clk.c, "msm_dmov"),
+
+ CLK_LOOKUP("pll0_clk", pll0_clk.c, "acpu"),
+ CLK_LOOKUP("pll1_clk", pll1_clk.c, "acpu"),
+ CLK_LOOKUP("pll2_clk", pll2_clk.c, "acpu"),
};
struct clock_init_data msm7x27_clock_init_data __initdata = {
.table = msm_clocks_7x27,
.size = ARRAY_SIZE(msm_clocks_7x27),
+ .init = msm_shared_pll_control_init,
};
-static struct clk_lookup msm_clocks_7x27a[] = {
+/* Clock table for common clocks between 7627a and 7625a */
+static struct clk_lookup msm_cmn_clk_7625a_7627a[] __initdata = {
CLK_LOOKUP("core_clk", adm_clk.c, "msm_dmov"),
CLK_LOOKUP("adsp_clk", adsp_clk.c, NULL),
CLK_LOOKUP("ahb_m_clk", ahb_m_clk.c, NULL),
@@ -340,11 +393,41 @@
CLK_LOOKUP("ebi1_mddi_clk", ebi_mddi_clk.c, NULL),
CLK_LOOKUP("ebi1_vfe_clk", ebi_vfe_clk.c, NULL),
CLK_LOOKUP("mem_clk", ebi_adm_clk.c, "msm_dmov"),
+
+ CLK_LOOKUP("pll0_clk", pll0_clk.c, "acpu"),
+ CLK_LOOKUP("pll1_clk", pll1_clk.c, "acpu"),
+ CLK_LOOKUP("pll2_clk", pll2_clk.c, "acpu"),
+
};
+/* PLL 4 clock is available for 7627a target. */
+static struct clk_lookup msm_clk_7627a[] __initdata = {
+ CLK_LOOKUP("pll4_clk", pll4_clk.c, "acpu"),
+};
+
+static struct clk_lookup msm_clk_7627a_7625a[ARRAY_SIZE(msm_cmn_clk_7625a_7627a)
+ + ARRAY_SIZE(msm_clk_7627a)];
+
+static void __init msm7627a_clock_init(void)
+{
+ int size = ARRAY_SIZE(msm_cmn_clk_7625a_7627a);
+
+ /* Intialize shared PLL control structure */
+ msm_shared_pll_control_init();
+
+ memcpy(&msm_clk_7627a_7625a, &msm_cmn_clk_7625a_7627a,
+ sizeof(msm_cmn_clk_7625a_7627a));
+ if (!cpu_is_msm7x25a()) {
+ memcpy(&msm_clk_7627a_7625a[size],
+ &msm_clk_7627a, sizeof(msm_clk_7627a));
+ size += ARRAY_SIZE(msm_clk_7627a);
+ }
+ msm7x27a_clock_init_data.size = size;
+}
+
struct clock_init_data msm7x27a_clock_init_data __initdata = {
- .table = msm_clocks_7x27a,
- .size = ARRAY_SIZE(msm_clocks_7x27a),
+ .table = msm_clk_7627a_7625a,
+ .init = msm7627a_clock_init,
};
static struct clk_lookup msm_clocks_8x50[] = {
diff --git a/arch/arm/mach-msm/clock-pll.c b/arch/arm/mach-msm/clock-pll.c
new file mode 100644
index 0000000..f6b2b45
--- /dev/null
+++ b/arch/arm/mach-msm/clock-pll.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2012, 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/delay.h>
+#include <linux/err.h>
+#include <linux/remote_spinlock.h>
+
+#include <mach/socinfo.h>
+#include <mach/msm_iomap.h>
+
+#include "clock.h"
+#include "clock-pll.h"
+#include "smd_private.h"
+
+struct pll_rate {
+ unsigned int lvalue;
+ unsigned long rate;
+};
+
+static struct pll_rate pll_l_rate[] = {
+ {10, 196000000},
+ {12, 245760000},
+ {30, 589820000},
+ {38, 737280000},
+ {41, 800000000},
+ {50, 960000000},
+ {52, 1008000000},
+ {62, 1200000000},
+ {0, 0},
+};
+
+#define PLL_BASE 7
+
+struct shared_pll_control {
+ uint32_t version;
+ struct {
+ /*
+ * Denotes if the PLL is ON. Technically, this can be read
+ * directly from the PLL registers, but this feild is here,
+ * so let's use it.
+ */
+ uint32_t on;
+ /*
+ * One bit for each processor core. The application processor
+ * is allocated bit position 1. All other bits should be
+ * considered as votes from other processors.
+ */
+ uint32_t votes;
+ } pll[PLL_BASE + PLL_END];
+};
+
+static remote_spinlock_t pll_lock;
+static struct shared_pll_control *pll_control;
+
+void __init msm_shared_pll_control_init(void)
+{
+#define PLL_REMOTE_SPINLOCK_ID "S:7"
+ unsigned smem_size;
+
+ remote_spin_lock_init(&pll_lock, PLL_REMOTE_SPINLOCK_ID);
+
+ pll_control = smem_get_entry(SMEM_CLKREGIM_SOURCES, &smem_size);
+ if (!pll_control) {
+ pr_err("Can't find shared PLL control data structure!\n");
+ BUG();
+ /*
+ * There might be more PLLs than what the application processor knows
+ * about. But the index used for each PLL is guaranteed to remain the
+ * same.
+ */
+ } else if (smem_size < sizeof(struct shared_pll_control)) {
+ pr_err("Shared PLL control data"
+ "structure too small!\n");
+ BUG();
+ } else if (pll_control->version != 0xCCEE0001) {
+ pr_err("Shared PLL control version mismatch!\n");
+ BUG();
+ } else {
+ pr_info("Shared PLL control available.\n");
+ return;
+ }
+
+}
+
+static void pll_enable(void __iomem *addr, unsigned on)
+{
+ if (on) {
+ writel_relaxed(2, addr);
+ mb();
+ udelay(5);
+ writel_relaxed(6, addr);
+ mb();
+ udelay(50);
+ writel_relaxed(7, addr);
+ } else {
+ writel_relaxed(0, addr);
+ }
+}
+
+static int pll_clk_enable(struct clk *clk)
+{
+ struct pll_shared_clk *pll = to_pll_shared_clk(clk);
+ unsigned int pll_id = pll->id;
+
+ remote_spin_lock(&pll_lock);
+
+ pll_control->pll[PLL_BASE + pll_id].votes |= BIT(1);
+ if (!pll_control->pll[PLL_BASE + pll_id].on) {
+ pll_enable(pll->mode_reg, 1);
+ pll_control->pll[PLL_BASE + pll_id].on = 1;
+ }
+
+ remote_spin_unlock(&pll_lock);
+ return 0;
+}
+
+static void pll_clk_disable(struct clk *clk)
+{
+ struct pll_shared_clk *pll = to_pll_shared_clk(clk);
+ unsigned int pll_id = pll->id;
+
+ remote_spin_lock(&pll_lock);
+
+ pll_control->pll[PLL_BASE + pll_id].votes &= ~BIT(1);
+ if (pll_control->pll[PLL_BASE + pll_id].on
+ && !pll_control->pll[PLL_BASE + pll_id].votes) {
+ pll_enable(pll->mode_reg, 0);
+ pll_control->pll[PLL_BASE + pll_id].on = 0;
+ }
+
+ remote_spin_unlock(&pll_lock);
+}
+
+static int pll_clk_is_enabled(struct clk *clk)
+{
+ struct pll_shared_clk *pll = to_pll_shared_clk(clk);
+
+ return readl_relaxed(pll->mode_reg) & BIT(0);
+}
+
+static bool pll_clk_is_local(struct clk *clk)
+{
+ return true;
+}
+
+static int pll_clk_handoff(struct clk *clk)
+{
+ struct pll_shared_clk *pll = to_pll_shared_clk(clk);
+ unsigned int pll_lval;
+ struct pll_rate *l;
+
+ /*
+ * Wait for the PLLs to be initialized and then read their frequency.
+ */
+ do {
+ pll_lval = readl_relaxed(pll->mode_reg + 4) & 0x3ff;
+ cpu_relax();
+ udelay(50);
+ } while (pll_lval == 0);
+
+ /* Convert PLL L values to PLL Output rate */
+ for (l = pll_l_rate; l->rate != 0; l++) {
+ if (l->lvalue == pll_lval) {
+ clk->rate = l->rate;
+ break;
+ }
+ }
+
+ if (!clk->rate) {
+ pr_crit("Unknown PLL's L value!\n");
+ BUG();
+ }
+
+ return 0;
+}
+
+struct clk_ops clk_pll_ops = {
+ .enable = pll_clk_enable,
+ .disable = pll_clk_disable,
+ .handoff = pll_clk_handoff,
+ .is_local = pll_clk_is_local,
+ .is_enabled = pll_clk_is_enabled,
+};
diff --git a/arch/arm/mach-msm/clock-pll.h b/arch/arm/mach-msm/clock-pll.h
new file mode 100644
index 0000000..ae0ca17
--- /dev/null
+++ b/arch/arm/mach-msm/clock-pll.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ */
+
+/**
+ * enum - For PLL IDs
+ */
+enum {
+ PLL_TCXO = -1,
+ PLL_0 = 0,
+ PLL_1,
+ PLL_2,
+ PLL_3,
+ PLL_4,
+ PLL_END,
+};
+
+/**
+ * struct pll_shared_clk - PLL shared with other processors without
+ * any HW voting
+ * @id: PLL ID
+ * @mode_reg: enable register
+ * @parent: clock source
+ * @c: clk
+ */
+struct pll_shared_clk {
+ unsigned int id;
+ void __iomem *const mode_reg;
+ struct clk c;
+};
+
+extern struct clk_ops clk_pll_ops;
+
+static inline struct pll_shared_clk *to_pll_shared_clk(struct clk *clk)
+{
+ return container_of(clk, struct pll_shared_clk, c);
+}
+
+/**
+ * msm_shared_pll_control_init() - Initialize shared pll control structure
+ */
+void msm_shared_pll_control_init(void);