summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaniya Das <tdas@codeaurora.org>2016-01-07 18:14:25 +0530
committerRohit Vaswani <rvaswani@codeaurora.org>2016-03-01 13:00:24 -0800
commit1ec7358a6161bdfe0482e4bd0d2b4448eea54449 (patch)
tree13fabf9d4d7b06870ef3f0ac0031d04733b5967e
parentedf938dde9457d3c45a70ed3228c4c4ff61f1e11 (diff)
clk: msm: Add support for MSM clocks
Support added for MSM clock and modifications in the clk framework to use the MSM clock framework. Change-Id: Ibbcf0ffbf9d30dde2dcb0e943225ad95dd4e857d Signed-off-by: Taniya Das <tdas@codeaurora.org>
-rw-r--r--arch/arm64/Kconfig2
-rw-r--r--arch/arm64/Kconfig.platforms4
-rw-r--r--drivers/clk/Kconfig3
-rw-r--r--drivers/clk/Makefile3
-rw-r--r--drivers/clk/clk.c18
-rw-r--r--drivers/clk/clk.h2
-rw-r--r--drivers/clk/clkdev.c12
-rw-r--r--drivers/clk/msm/Kconfig5
-rw-r--r--drivers/clk/msm/Makefile14
-rw-r--r--drivers/clk/msm/clock-alpha-pll.c1214
-rw-r--r--drivers/clk/msm/clock-debug.c676
-rw-r--r--drivers/clk/msm/clock-dummy.c113
-rw-r--r--drivers/clk/msm/clock-generic.c909
-rw-r--r--drivers/clk/msm/clock-local2.c2682
-rw-r--r--drivers/clk/msm/clock-pll.c1205
-rw-r--r--drivers/clk/msm/clock-rpm.c472
-rw-r--r--drivers/clk/msm/clock-voter.c202
-rw-r--r--drivers/clk/msm/clock.c1389
-rw-r--r--drivers/clk/msm/clock.h52
-rw-r--r--drivers/clk/msm/gdsc.c639
-rw-r--r--drivers/clk/msm/msm-clock-controller.c755
-rw-r--r--include/linux/clk-provider.h7
-rw-r--r--include/linux/clk.h2
-rw-r--r--include/linux/clk/gdsc.h22
-rw-r--r--include/linux/clk/msm-clk-provider.h268
-rw-r--r--include/linux/clk/msm-clk.h122
-rw-r--r--include/linux/clk/msm-clock-generic.h305
-rw-r--r--include/linux/clkdev.h1
-rw-r--r--include/soc/qcom/clock-alpha-pll.h94
-rw-r--r--include/soc/qcom/clock-local2.h256
-rw-r--r--include/soc/qcom/clock-pll.h231
-rw-r--r--include/soc/qcom/clock-rpm.h180
-rw-r--r--include/soc/qcom/clock-voter.h51
-rw-r--r--include/soc/qcom/msm-clock-controller.h144
34 files changed, 12041 insertions, 13 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index beea70ba7995..a24e372c842a 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -23,7 +23,7 @@ config ARM64
select ARM_PSCI_FW
select BUILDTIME_EXTABLE_SORT
select CLONE_BACKWARDS
- select COMMON_CLK
+ select COMMON_CLK if !ARCH_QCOM
select CPU_PM if (SUSPEND || CPU_IDLE)
select DCACHE_WORD_ACCESS
select EDAC_SUPPORT
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 4043c35962cc..227870c95f7e 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -49,6 +49,10 @@ config ARCH_MEDIATEK
config ARCH_QCOM
bool "Qualcomm Platforms"
select PINCTRL
+ select CLKDEV_LOOKUP
+ select HAVE_CLK
+ select HAVE_CLK_PREPARE
+ select PM_OPP
help
This enables support for the ARMv8 based Qualcomm chipsets.
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c3e3a02f7f1f..b00e391238b8 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -42,7 +42,7 @@ config COMMON_CLK_MAX77686
depends on MFD_MAX77686
select COMMON_CLK_MAX_GEN
---help---
- This driver supports Maxim 77686 crystal oscillator clock.
+ This driver supports Maxim 77686 crystal oscillator clock.
config COMMON_CLK_MAX77802
tristate "Clock driver for Maxim 77802 PMIC"
@@ -198,3 +198,4 @@ source "drivers/clk/mvebu/Kconfig"
source "drivers/clk/samsung/Kconfig"
source "drivers/clk/tegra/Kconfig"
+source "drivers/clk/msm/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 820714c72d36..37f67c77fe7c 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,7 +1,7 @@
# common clock types
obj-$(CONFIG_HAVE_CLK) += clk-devres.o
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
-obj-$(CONFIG_COMMON_CLK) += clk.o
+obj-$(CONFIG_OF) += clk.o
obj-$(CONFIG_COMMON_CLK) += clk-divider.o
obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o
obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
@@ -82,3 +82,4 @@ obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_ARCH_ZX) += zte/
obj-$(CONFIG_ARCH_ZYNQ) += zynq/
obj-$(CONFIG_H8300) += h8300/
+obj-$(CONFIG_ARCH_QCOM) += msm/
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index f13c3f4228d4..cd54a184e997 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -26,6 +26,8 @@
#include "clk.h"
+#if defined(CONFIG_COMMON_CLK)
+
static DEFINE_SPINLOCK(enable_lock);
static DEFINE_MUTEX(prepare_lock);
@@ -2901,6 +2903,8 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
}
EXPORT_SYMBOL_GPL(clk_notifier_unregister);
+#endif /* CONFIG_COMMON_CLK */
+
#ifdef CONFIG_OF
/**
* struct of_clk_provider - Clock provider registration structure
@@ -2931,6 +2935,8 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
}
EXPORT_SYMBOL_GPL(of_clk_src_simple_get);
+#if defined(CONFIG_COMMON_CLK)
+
struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
{
struct clk_onecell_data *clk_data = data;
@@ -2945,6 +2951,11 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
}
EXPORT_SYMBOL_GPL(of_clk_src_onecell_get);
+#endif /* CONFIG_COMMON_CLK */
+
+/* forward declaration */
+void of_clk_del_provider(struct device_node *np);
+
/**
* of_clk_add_provider() - Register a clock provider for a node
* @np: Device node pointer associated with clock provider
@@ -3100,8 +3111,10 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
else
clk_name = NULL;
} else {
+#if defined(CONFIG_COMMON_CLK)
clk_name = __clk_get_name(clk);
clk_put(clk);
+#endif
}
}
@@ -3132,6 +3145,8 @@ int of_clk_parent_fill(struct device_node *np, const char **parents,
}
EXPORT_SYMBOL_GPL(of_clk_parent_fill);
+#if defined(CONFIG_COMMON_CLK)
+
struct clock_provider {
of_clk_init_cb_t clk_init_cb;
struct device_node *np;
@@ -3240,4 +3255,7 @@ void __init of_clk_init(const struct of_device_id *matches)
force = true;
}
}
+
+#endif /* CONFIG_COMMON_CLK */
+
#endif
diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h
index 00b35a13cdf3..97941b0f8f32 100644
--- a/drivers/clk/clk.h
+++ b/drivers/clk/clk.h
@@ -11,7 +11,7 @@
struct clk_hw;
-#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
+#if defined(CONFIG_OF)
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
const char *dev_id, const char *con_id);
#endif
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 779b6ff0c7ad..ee1a9992c47f 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -27,7 +27,7 @@
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
-#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
+#if defined(CONFIG_OF)
static struct clk *__of_clk_get(struct device_node *np, int index,
const char *dev_id, const char *con_id)
{
@@ -73,14 +73,10 @@ static struct clk *__of_clk_get_by_name(struct device_node *np,
if (name)
index = of_property_match_string(np, "clock-names", name);
clk = __of_clk_get(np, index, dev_id, name);
- if (!IS_ERR(clk)) {
+ if (!IS_ERR(clk))
break;
- } else if (name && index >= 0) {
- if (PTR_ERR(clk) != -EPROBE_DEFER)
- pr_err("ERROR: could not get clock %s:%s(%i)\n",
- np->full_name, name ? name : "", index);
+ else if (name && index >= 0)
return clk;
- }
/*
* No matching clock found on this node. If the parent node
@@ -190,7 +186,7 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id)
out:
mutex_unlock(&clocks_mutex);
- return cl ? clk : ERR_PTR(-ENOENT);
+ return cl ? cl->clk : ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL(clk_get_sys);
diff --git a/drivers/clk/msm/Kconfig b/drivers/clk/msm/Kconfig
new file mode 100644
index 000000000000..7928fc7cfd85
--- /dev/null
+++ b/drivers/clk/msm/Kconfig
@@ -0,0 +1,5 @@
+config MSM_CLK_CONTROLLER_V2
+ bool "QTI clock driver"
+ ---help---
+ Generate clock data structures from definitions found in
+ device tree.
diff --git a/drivers/clk/msm/Makefile b/drivers/clk/msm/Makefile
new file mode 100644
index 000000000000..1e865e033810
--- /dev/null
+++ b/drivers/clk/msm/Makefile
@@ -0,0 +1,14 @@
+obj-y += clock.o
+obj-y += clock-dummy.o
+obj-y += clock-generic.o
+obj-y += clock-local2.o
+obj-y += clock-pll.o
+obj-y += clock-alpha-pll.o
+obj-y += clock-rpm.o
+obj-y += clock-voter.o
+
+obj-$(CONFIG_MSM_CLK_CONTROLLER_V2) += msm-clock-controller.o
+
+obj-y += clock-debug.o
+
+obj-y += gdsc.o
diff --git a/drivers/clk/msm/clock-alpha-pll.c b/drivers/clk/msm/clock-alpha-pll.c
new file mode 100644
index 000000000000..ad1f62467771
--- /dev/null
+++ b/drivers/clk/msm/clock-alpha-pll.c
@@ -0,0 +1,1214 @@
+/*
+ * Copyright (c) 2012-2015, 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/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <soc/qcom/clock-alpha-pll.h>
+#include <soc/qcom/msm-clock-controller.h>
+
+#include "clock.h"
+
+#define WAIT_MAX_LOOPS 100
+
+#define MODE_REG(pll) (*pll->base + pll->offset + 0x0)
+#define LOCK_REG(pll) (*pll->base + pll->offset + 0x0)
+#define ACTIVE_REG(pll) (*pll->base + pll->offset + 0x0)
+#define UPDATE_REG(pll) (*pll->base + pll->offset + 0x0)
+#define L_REG(pll) (*pll->base + pll->offset + 0x4)
+#define A_REG(pll) (*pll->base + pll->offset + 0x8)
+#define VCO_REG(pll) (*pll->base + pll->offset + 0x10)
+#define ALPHA_EN_REG(pll) (*pll->base + pll->offset + 0x10)
+#define OUTPUT_REG(pll) (*pll->base + pll->offset + 0x10)
+#define VOTE_REG(pll) (*pll->base + pll->fsm_reg_offset)
+#define USER_CTL_LO_REG(pll) (*pll->base + pll->offset + 0x10)
+#define USER_CTL_HI_REG(pll) (*pll->base + pll->offset + 0x14)
+#define CONFIG_CTL_REG(pll) (*pll->base + pll->offset + 0x18)
+#define TEST_CTL_LO_REG(pll) (*pll->base + pll->offset + 0x1c)
+#define TEST_CTL_HI_REG(pll) (*pll->base + pll->offset + 0x20)
+
+#define PLL_BYPASSNL 0x2
+#define PLL_RESET_N 0x4
+#define PLL_OUTCTRL 0x1
+#define PLL_LATCH_INTERFACE BIT(11)
+
+#define FABIA_CONFIG_CTL_REG(pll) (*pll->base + pll->offset + 0x14)
+#define FABIA_USER_CTL_LO_REG(pll) (*pll->base + pll->offset + 0xc)
+#define FABIA_USER_CTL_HI_REG(pll) (*pll->base + pll->offset + 0x10)
+#define FABIA_TEST_CTL_LO_REG(pll) (*pll->base + pll->offset + 0x1c)
+#define FABIA_TEST_CTL_HI_REG(pll) (*pll->base + pll->offset + 0x20)
+#define FABIA_L_REG(pll) (*pll->base + pll->offset + 0x4)
+#define FABIA_FRAC_REG(pll) (*pll->base + pll->offset + 0x38)
+#define FABIA_PLL_OPMODE(pll) (*pll->base + pll->offset + 0x2c)
+
+#define FABIA_PLL_STANDBY 0x0
+#define FABIA_PLL_RUN 0x1
+#define FABIA_PLL_OUT_MAIN 0x7
+#define FABIA_RATE_MARGIN 500
+#define FABIA_PLL_ACK_LATCH BIT(29)
+#define FABIA_PLL_HW_UPDATE_LOGIC_BYPASS BIT(23)
+
+/*
+ * Even though 40 bits are present, use only 32 for ease of calculation.
+ */
+#define ALPHA_REG_BITWIDTH 40
+#define ALPHA_BITWIDTH 32
+#define FABIA_ALPHA_BITWIDTH 16
+
+/*
+ * Enable/disable registers could be shared among PLLs when FSM voting
+ * is used. This lock protects against potential race when multiple
+ * PLLs are being enabled/disabled together.
+ */
+static DEFINE_SPINLOCK(alpha_pll_reg_lock);
+
+static unsigned long compute_rate(struct alpha_pll_clk *pll,
+ u32 l_val, u32 a_val)
+{
+ u64 rate, parent_rate;
+ int alpha_bw = ALPHA_BITWIDTH;
+
+ if (pll->is_fabia)
+ alpha_bw = FABIA_ALPHA_BITWIDTH;
+
+ parent_rate = clk_get_rate(pll->c.parent);
+ rate = parent_rate * l_val;
+ rate += (parent_rate * a_val) >> alpha_bw;
+ return rate;
+}
+
+static bool is_locked(struct alpha_pll_clk *pll)
+{
+ u32 reg = readl_relaxed(LOCK_REG(pll));
+ u32 mask = pll->masks->lock_mask;
+ return (reg & mask) == mask;
+}
+
+static bool is_active(struct alpha_pll_clk *pll)
+{
+ u32 reg = readl_relaxed(ACTIVE_REG(pll));
+ u32 mask = pll->masks->active_mask;
+ return (reg & mask) == mask;
+}
+
+/*
+ * Check active_flag if PLL is in FSM mode, otherwise check lock_det
+ * bit. This function assumes PLLs are already configured to the
+ * right mode.
+ */
+static bool update_finish(struct alpha_pll_clk *pll)
+{
+ if (pll->fsm_en_mask)
+ return is_active(pll);
+ else
+ return is_locked(pll);
+}
+
+static int wait_for_update(struct alpha_pll_clk *pll)
+{
+ int count;
+
+ for (count = WAIT_MAX_LOOPS; count > 0; count--) {
+ if (update_finish(pll))
+ break;
+ udelay(1);
+ }
+
+ if (!count) {
+ pr_err("%s didn't lock after enabling it!\n", pll->c.dbg_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __alpha_pll_vote_enable(struct alpha_pll_clk *pll)
+{
+ u32 ena;
+
+ ena = readl_relaxed(VOTE_REG(pll));
+ ena |= pll->fsm_en_mask;
+ writel_relaxed(ena, VOTE_REG(pll));
+ mb();
+
+ return wait_for_update(pll);
+}
+
+static int __alpha_pll_enable(struct alpha_pll_clk *pll, int enable_output)
+{
+ int rc;
+ u32 mode;
+
+ mode = readl_relaxed(MODE_REG(pll));
+ mode |= PLL_BYPASSNL;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset.
+ */
+ mb();
+ udelay(5);
+
+ mode |= PLL_RESET_N;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ rc = wait_for_update(pll);
+ if (rc < 0)
+ return rc;
+
+ /* Enable PLL output. */
+ if (enable_output) {
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, MODE_REG(pll));
+ }
+
+ /* Ensure that the write above goes through before returning. */
+ mb();
+ return 0;
+}
+
+static void setup_alpha_pll_values(u64 a_val, u32 l_val, u32 vco_val,
+ struct alpha_pll_clk *pll)
+{
+ struct alpha_pll_masks *masks = pll->masks;
+ u32 regval;
+
+ a_val = a_val << (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
+
+ writel_relaxed(l_val, L_REG(pll));
+ __iowrite32_copy(A_REG(pll), &a_val, 2);
+
+ if (vco_val != UINT_MAX) {
+ regval = readl_relaxed(VCO_REG(pll));
+ regval &= ~(masks->vco_mask << masks->vco_shift);
+ regval |= vco_val << masks->vco_shift;
+ writel_relaxed(regval, VCO_REG(pll));
+ }
+
+ regval = readl_relaxed(ALPHA_EN_REG(pll));
+ regval |= masks->alpha_en_mask;
+ writel_relaxed(regval, ALPHA_EN_REG(pll));
+}
+
+static int alpha_pll_enable(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ unsigned long flags;
+ int rc;
+
+ if (unlikely(!pll->inited))
+ __init_alpha_pll(c);
+
+ spin_lock_irqsave(&alpha_pll_reg_lock, flags);
+ if (pll->fsm_en_mask)
+ rc = __alpha_pll_vote_enable(pll);
+ else
+ rc = __alpha_pll_enable(pll, true);
+ spin_unlock_irqrestore(&alpha_pll_reg_lock, flags);
+
+ return rc;
+}
+
+static int __calibrate_alpha_pll(struct alpha_pll_clk *pll);
+static int dyna_alpha_pll_enable(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ unsigned long flags;
+ int rc;
+
+ if (unlikely(!pll->inited))
+ __init_alpha_pll(c);
+
+ spin_lock_irqsave(&alpha_pll_reg_lock, flags);
+
+ if (pll->slew)
+ __calibrate_alpha_pll(pll);
+
+ if (pll->fsm_en_mask)
+ rc = __alpha_pll_vote_enable(pll);
+ else
+ rc = __alpha_pll_enable(pll, true);
+ spin_unlock_irqrestore(&alpha_pll_reg_lock, flags);
+
+ return rc;
+}
+
+#define PLL_OFFLINE_REQ_BIT BIT(7)
+#define PLL_FSM_ENA_BIT BIT(20)
+#define PLL_OFFLINE_ACK_BIT BIT(28)
+#define PLL_ACTIVE_FLAG BIT(30)
+
+static int alpha_pll_enable_hwfsm(struct clk *c)
+{
+ u32 mode;
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+
+ /* Re-enable HW FSM mode, clear OFFLINE request */
+ mode = readl_relaxed(MODE_REG(pll));
+ mode |= PLL_FSM_ENA_BIT;
+ mode &= ~PLL_OFFLINE_REQ_BIT;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ /* Make sure enable request goes through before waiting for update */
+ mb();
+
+ if (wait_for_update(pll) < 0)
+ panic("PLL %s failed to lock", c->dbg_name);
+
+ return 0;
+}
+
+static void alpha_pll_disable_hwfsm(struct clk *c)
+{
+ u32 mode;
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+
+ /* Request PLL_OFFLINE and wait for ack */
+ mode = readl_relaxed(MODE_REG(pll));
+ writel_relaxed(mode | PLL_OFFLINE_REQ_BIT, MODE_REG(pll));
+ while (!(readl_relaxed(MODE_REG(pll)) & PLL_OFFLINE_ACK_BIT))
+ ;
+
+ /* Disable HW FSM */
+ mode = readl_relaxed(MODE_REG(pll));
+ mode &= ~PLL_FSM_ENA_BIT;
+ if (pll->offline_bit_workaround)
+ mode &= ~PLL_OFFLINE_REQ_BIT;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ while (readl_relaxed(MODE_REG(pll)) & PLL_ACTIVE_FLAG)
+ ;
+}
+
+static void __alpha_pll_vote_disable(struct alpha_pll_clk *pll)
+{
+ u32 ena;
+
+ ena = readl_relaxed(VOTE_REG(pll));
+ ena &= ~pll->fsm_en_mask;
+ writel_relaxed(ena, VOTE_REG(pll));
+}
+
+static void __alpha_pll_disable(struct alpha_pll_clk *pll)
+{
+ u32 mode;
+
+ mode = readl_relaxed(MODE_REG(pll));
+ mode &= ~PLL_OUTCTRL;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ /* Delay of 2 output clock ticks required until output is disabled */
+ mb();
+ udelay(1);
+
+ mode &= ~(PLL_BYPASSNL | PLL_RESET_N);
+ writel_relaxed(mode, MODE_REG(pll));
+}
+
+static void alpha_pll_disable(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ unsigned long flags;
+
+ spin_lock_irqsave(&alpha_pll_reg_lock, flags);
+ if (pll->fsm_en_mask)
+ __alpha_pll_vote_disable(pll);
+ else
+ __alpha_pll_disable(pll);
+ spin_unlock_irqrestore(&alpha_pll_reg_lock, flags);
+}
+
+static void dyna_alpha_pll_disable(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ unsigned long flags;
+
+ spin_lock_irqsave(&alpha_pll_reg_lock, flags);
+ if (pll->fsm_en_mask)
+ __alpha_pll_vote_disable(pll);
+ else
+ __alpha_pll_disable(pll);
+
+ spin_unlock_irqrestore(&alpha_pll_reg_lock, flags);
+}
+
+static u32 find_vco(struct alpha_pll_clk *pll, unsigned long rate)
+{
+ unsigned long i;
+ struct alpha_pll_vco_tbl *v = pll->vco_tbl;
+
+ for (i = 0; i < pll->num_vco; i++) {
+ if (rate >= v[i].min_freq && rate <= v[i].max_freq)
+ return v[i].vco_val;
+ }
+
+ return -EINVAL;
+}
+
+static unsigned long __calc_values(struct alpha_pll_clk *pll,
+ unsigned long rate, int *l_val, u64 *a_val, bool round_up)
+{
+ u32 parent_rate;
+ u64 remainder;
+ u64 quotient;
+ unsigned long freq_hz;
+ int alpha_bw = ALPHA_BITWIDTH;
+
+ parent_rate = clk_get_rate(pll->c.parent);
+ quotient = rate;
+ remainder = do_div(quotient, parent_rate);
+ *l_val = quotient;
+
+ if (!remainder) {
+ *a_val = 0;
+ return rate;
+ }
+
+ if (pll->is_fabia)
+ alpha_bw = FABIA_ALPHA_BITWIDTH;
+
+ /* Upper ALPHA_BITWIDTH bits of Alpha */
+ quotient = remainder << alpha_bw;
+ remainder = do_div(quotient, parent_rate);
+
+ if (remainder && round_up)
+ quotient++;
+
+ *a_val = quotient;
+ freq_hz = compute_rate(pll, *l_val, *a_val);
+ return freq_hz;
+}
+
+static unsigned long round_rate_down(struct alpha_pll_clk *pll,
+ unsigned long rate, int *l_val, u64 *a_val)
+{
+ return __calc_values(pll, rate, l_val, a_val, false);
+}
+
+static unsigned long round_rate_up(struct alpha_pll_clk *pll,
+ unsigned long rate, int *l_val, u64 *a_val)
+{
+ return __calc_values(pll, rate, l_val, a_val, true);
+}
+
+static bool dynamic_update_finish(struct alpha_pll_clk *pll)
+{
+ u32 reg = readl_relaxed(UPDATE_REG(pll));
+ u32 mask = pll->masks->update_mask;
+
+ return (reg & mask) == 0;
+}
+
+static int wait_for_dynamic_update(struct alpha_pll_clk *pll)
+{
+ int count;
+
+ for (count = WAIT_MAX_LOOPS; count > 0; count--) {
+ if (dynamic_update_finish(pll))
+ break;
+ udelay(1);
+ }
+
+ if (!count) {
+ pr_err("%s didn't latch after updating it!\n", pll->c.dbg_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dyna_alpha_pll_dynamic_update(struct alpha_pll_clk *pll)
+{
+ struct alpha_pll_masks *masks = pll->masks;
+ u32 regval;
+ int rc;
+
+ regval = readl_relaxed(UPDATE_REG(pll));
+ regval |= masks->update_mask;
+ writel_relaxed(regval, UPDATE_REG(pll));
+
+ rc = wait_for_dynamic_update(pll);
+ if (rc < 0)
+ return rc;
+
+ /*
+ * HPG mandates a wait of at least 570ns before polling the LOCK
+ * detect bit. Have a delay of 1us just to be safe.
+ */
+ mb();
+ udelay(1);
+
+ rc = wait_for_update(pll);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int alpha_pll_set_rate(struct clk *c, unsigned long rate);
+static int dyna_alpha_pll_set_rate(struct clk *c, unsigned long rate)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ unsigned long freq_hz, flags;
+ u32 l_val, vco_val;
+ u64 a_val;
+ int ret;
+
+ freq_hz = round_rate_up(pll, rate, &l_val, &a_val);
+ if (freq_hz != rate) {
+ pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n");
+ return -EINVAL;
+ }
+
+ vco_val = find_vco(pll, freq_hz);
+
+ /*
+ * Dynamic pll update will not support switching frequencies across
+ * vco ranges. In those cases fall back to normal alpha set rate.
+ */
+ if (pll->current_vco_val != vco_val) {
+ ret = alpha_pll_set_rate(c, rate);
+ if (!ret)
+ pll->current_vco_val = vco_val;
+ else
+ return ret;
+ return 0;
+ }
+
+ spin_lock_irqsave(&c->lock, flags);
+
+ a_val = a_val << (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
+
+ writel_relaxed(l_val, L_REG(pll));
+ __iowrite32_copy(A_REG(pll), &a_val, 2);
+
+ /* Ensure that the write above goes through before proceeding. */
+ mb();
+
+ if (c->count)
+ dyna_alpha_pll_dynamic_update(pll);
+
+ spin_unlock_irqrestore(&c->lock, flags);
+ return 0;
+}
+
+/*
+ * Slewing plls should be bought up at frequency which is in the middle of the
+ * desired VCO range. So after bringing up the pll at calibration freq, set it
+ * back to desired frequency(that was set by previous clk_set_rate).
+ */
+static int __calibrate_alpha_pll(struct alpha_pll_clk *pll)
+{
+ unsigned long calibration_freq, freq_hz;
+ struct alpha_pll_vco_tbl *vco_tbl = pll->vco_tbl;
+ u64 a_val;
+ u32 l_val, vco_val;
+ int rc;
+
+ vco_val = find_vco(pll, pll->c.rate);
+ if (IS_ERR_VALUE(vco_val)) {
+ pr_err("alpha pll: not in a valid vco range\n");
+ return -EINVAL;
+ }
+ calibration_freq = (vco_tbl[vco_val].min_freq +
+ vco_tbl[vco_val].max_freq)/2;
+
+ freq_hz = round_rate_up(pll, calibration_freq, &l_val, &a_val);
+ if (freq_hz != calibration_freq) {
+ pr_err("alpha_pll: call clk_set_rate with rounded rates!\n");
+ return -EINVAL;
+ }
+
+ setup_alpha_pll_values(a_val, l_val, vco_tbl->vco_val, pll);
+
+ /* Bringup the pll at calibration frequency */
+ rc = __alpha_pll_enable(pll, false);
+ if (rc) {
+ pr_err("alpha pll calibration failed\n");
+ return rc;
+ }
+
+ /*
+ * PLL is already running at calibration frequency.
+ * So slew pll to the previously set frequency.
+ */
+ pr_debug("pll %s: setting back to required rate %lu\n", pll->c.dbg_name,
+ pll->c.rate);
+ freq_hz = round_rate_up(pll, pll->c.rate, &l_val, &a_val);
+ setup_alpha_pll_values(a_val, l_val, UINT_MAX, pll);
+ dyna_alpha_pll_dynamic_update(pll);
+
+ return 0;
+}
+
+static int alpha_pll_set_rate(struct clk *c, unsigned long rate)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ struct alpha_pll_masks *masks = pll->masks;
+ unsigned long flags, freq_hz;
+ u32 regval, l_val;
+ int vco_val;
+ u64 a_val;
+
+ freq_hz = round_rate_up(pll, rate, &l_val, &a_val);
+ if (freq_hz != rate) {
+ pr_err("alpha_pll: Call clk_set_rate with rounded rates!\n");
+ return -EINVAL;
+ }
+
+ vco_val = find_vco(pll, freq_hz);
+ if (IS_ERR_VALUE(vco_val)) {
+ pr_err("alpha pll: not in a valid vco range\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Ensure PLL is off before changing rate. For optimization reasons,
+ * assume no downstream clock is actively using it. No support
+ * for dynamic update at the moment.
+ */
+ spin_lock_irqsave(&c->lock, flags);
+ if (c->count)
+ c->ops->disable(c);
+
+ a_val = a_val << (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
+
+ writel_relaxed(l_val, L_REG(pll));
+ __iowrite32_copy(A_REG(pll), &a_val, 2);
+
+ if (masks->vco_mask) {
+ regval = readl_relaxed(VCO_REG(pll));
+ regval &= ~(masks->vco_mask << masks->vco_shift);
+ regval |= vco_val << masks->vco_shift;
+ writel_relaxed(regval, VCO_REG(pll));
+ }
+
+ regval = readl_relaxed(ALPHA_EN_REG(pll));
+ regval |= masks->alpha_en_mask;
+ writel_relaxed(regval, ALPHA_EN_REG(pll));
+
+ if (c->count)
+ c->ops->enable(c);
+
+ spin_unlock_irqrestore(&c->lock, flags);
+ return 0;
+}
+
+static long alpha_pll_round_rate(struct clk *c, unsigned long rate)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ struct alpha_pll_vco_tbl *v = pll->vco_tbl;
+ int ret;
+ u32 l_val;
+ unsigned long freq_hz;
+ u64 a_val;
+ int i;
+
+ if (pll->no_prepared_reconfig && c->prepare_count)
+ return -EINVAL;
+
+ freq_hz = round_rate_up(pll, rate, &l_val, &a_val);
+ if (pll->is_fabia) {
+ if (rate < pll->min_supported_freq)
+ return pll->min_supported_freq;
+ return freq_hz;
+ }
+ ret = find_vco(pll, freq_hz);
+ if (!IS_ERR_VALUE(ret))
+ return freq_hz;
+
+ freq_hz = 0;
+ for (i = 0; i < pll->num_vco; i++) {
+ if (is_better_rate(rate, freq_hz, v[i].min_freq))
+ freq_hz = v[i].min_freq;
+ if (is_better_rate(rate, freq_hz, v[i].max_freq))
+ freq_hz = v[i].max_freq;
+ }
+ if (!freq_hz)
+ return -EINVAL;
+ return freq_hz;
+}
+
+static void update_vco_tbl(struct alpha_pll_clk *pll)
+{
+ int i, l_val;
+ u64 a_val;
+ unsigned long hz;
+
+ /* Round vco limits to valid rates */
+ for (i = 0; i < pll->num_vco; i++) {
+ hz = round_rate_up(pll, pll->vco_tbl[i].min_freq, &l_val,
+ &a_val);
+ pll->vco_tbl[i].min_freq = hz;
+
+ hz = round_rate_down(pll, pll->vco_tbl[i].max_freq, &l_val,
+ &a_val);
+ pll->vco_tbl[i].max_freq = hz;
+ }
+}
+
+/*
+ * Program bias count to be 0x6 (corresponds to 5us), and lock count
+ * bits to 0 (check lock_det for locking).
+ */
+static void __set_fsm_mode(void __iomem *mode_reg)
+{
+ u32 regval = readl_relaxed(mode_reg);
+
+ /* De-assert reset to FSM */
+ regval &= ~BIT(21);
+ writel_relaxed(regval, mode_reg);
+
+ /* Program bias count */
+ regval &= ~BM(19, 14);
+ regval |= BVAL(19, 14, 0x6);
+ writel_relaxed(regval, mode_reg);
+
+ /* Program lock count */
+ regval &= ~BM(13, 8);
+ regval |= BVAL(13, 8, 0x0);
+ writel_relaxed(regval, mode_reg);
+
+ /* Enable PLL FSM voting */
+ regval |= BIT(20);
+ writel_relaxed(regval, mode_reg);
+}
+
+static bool is_fsm_mode(void __iomem *mode_reg)
+{
+ return !!(readl_relaxed(mode_reg) & BIT(20));
+}
+
+void __init_alpha_pll(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ struct alpha_pll_masks *masks = pll->masks;
+ u32 regval;
+
+ if (pll->config_ctl_val)
+ writel_relaxed(pll->config_ctl_val, CONFIG_CTL_REG(pll));
+
+ if (masks->output_mask && pll->enable_config) {
+ regval = readl_relaxed(OUTPUT_REG(pll));
+ regval &= ~masks->output_mask;
+ regval |= pll->enable_config;
+ writel_relaxed(regval, OUTPUT_REG(pll));
+ }
+
+ if (masks->post_div_mask) {
+ regval = readl_relaxed(USER_CTL_LO_REG(pll));
+ regval &= ~masks->post_div_mask;
+ regval |= pll->post_div_config;
+ writel_relaxed(regval, USER_CTL_LO_REG(pll));
+ }
+
+ if (pll->slew) {
+ regval = readl_relaxed(USER_CTL_HI_REG(pll));
+ regval &= ~PLL_LATCH_INTERFACE;
+ writel_relaxed(regval, USER_CTL_HI_REG(pll));
+ }
+
+ if (masks->test_ctl_lo_mask) {
+ regval = readl_relaxed(TEST_CTL_LO_REG(pll));
+ regval &= ~masks->test_ctl_lo_mask;
+ regval |= pll->test_ctl_lo_val;
+ writel_relaxed(regval, TEST_CTL_LO_REG(pll));
+ }
+
+ if (masks->test_ctl_hi_mask) {
+ regval = readl_relaxed(TEST_CTL_HI_REG(pll));
+ regval &= ~masks->test_ctl_hi_mask;
+ regval |= pll->test_ctl_hi_val;
+ writel_relaxed(regval, TEST_CTL_HI_REG(pll));
+ }
+
+ if (pll->fsm_en_mask)
+ __set_fsm_mode(MODE_REG(pll));
+
+ pll->inited = true;
+}
+
+static enum handoff alpha_pll_handoff(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ struct alpha_pll_masks *masks = pll->masks;
+ u64 a_val;
+ u32 alpha_en, l_val;
+
+ update_vco_tbl(pll);
+
+ if (!is_locked(pll)) {
+ if (c->rate && alpha_pll_set_rate(c, c->rate))
+ WARN(1, "%s: Failed to configure rate\n", c->dbg_name);
+ __init_alpha_pll(c);
+ return HANDOFF_DISABLED_CLK;
+ } else if (pll->fsm_en_mask && !is_fsm_mode(MODE_REG(pll))) {
+ WARN(1, "%s should be in FSM mode but is not\n", c->dbg_name);
+ }
+
+ l_val = readl_relaxed(L_REG(pll));
+ /* read u64 in two steps to satisfy alignment constraint */
+ a_val = readl_relaxed(A_REG(pll) + 0x4);
+ a_val = a_val << 32 | readl_relaxed(A_REG(pll));
+ /* get upper 32 bits */
+ a_val = a_val >> (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
+
+ alpha_en = readl_relaxed(ALPHA_EN_REG(pll));
+ alpha_en &= masks->alpha_en_mask;
+ if (!alpha_en)
+ a_val = 0;
+
+ c->rate = compute_rate(pll, l_val, a_val);
+
+ /*
+ * Unconditionally vote for the PLL; it might be on because of
+ * another master's vote.
+ */
+ if (pll->fsm_en_mask)
+ __alpha_pll_vote_enable(pll);
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static void __iomem *alpha_pll_list_registers(struct clk *clk, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(clk);
+ static struct clk_register_data data[] = {
+ {"PLL_MODE", 0x0},
+ {"PLL_L_VAL", 0x4},
+ {"PLL_ALPHA_VAL", 0x8},
+ {"PLL_ALPHA_VAL_U", 0xC},
+ {"PLL_USER_CTL", 0x10},
+ {"PLL_CONFIG_CTL", 0x18},
+ };
+
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return MODE_REG(pll);
+}
+
+static int __fabia_alpha_pll_enable(struct alpha_pll_clk *pll)
+{
+ int rc;
+ u32 mode;
+
+ /* Disable PLL output */
+ mode = readl_relaxed(MODE_REG(pll));
+ mode &= ~PLL_OUTCTRL;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ /* Set operation mode to STANDBY */
+ writel_relaxed(FABIA_PLL_STANDBY, FABIA_PLL_OPMODE(pll));
+
+ /* PLL should be in STANDBY mode before continuing */
+ mb();
+
+ /* Bring PLL out of reset */
+ mode = readl_relaxed(MODE_REG(pll));
+ mode |= PLL_RESET_N;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ /* Set operation mode to RUN */
+ writel_relaxed(FABIA_PLL_RUN, FABIA_PLL_OPMODE(pll));
+
+ rc = wait_for_update(pll);
+ if (rc < 0)
+ return rc;
+
+ /* Enable the main PLL output */
+ mode = readl_relaxed(FABIA_USER_CTL_LO_REG(pll));
+ mode |= FABIA_PLL_OUT_MAIN;
+ writel_relaxed(mode, FABIA_USER_CTL_LO_REG(pll));
+
+ /* Enable PLL outputs */
+ mode = readl_relaxed(MODE_REG(pll));
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ /* Ensure that the write above goes through before returning. */
+ mb();
+ return 0;
+}
+
+static int fabia_alpha_pll_enable(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ unsigned long flags;
+ int rc;
+
+ spin_lock_irqsave(&alpha_pll_reg_lock, flags);
+ if (pll->fsm_en_mask)
+ rc = __alpha_pll_vote_enable(pll);
+ else
+ rc = __fabia_alpha_pll_enable(pll);
+ spin_unlock_irqrestore(&alpha_pll_reg_lock, flags);
+
+ return rc;
+}
+
+static void __fabia_alpha_pll_disable(struct alpha_pll_clk *pll)
+{
+ u32 mode;
+
+ /* Disable PLL outputs */
+ mode = readl_relaxed(MODE_REG(pll));
+ mode &= ~PLL_OUTCTRL;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ /* Disable the main PLL output */
+ mode = readl_relaxed(FABIA_USER_CTL_LO_REG(pll));
+ mode &= ~FABIA_PLL_OUT_MAIN;
+ writel_relaxed(mode, FABIA_USER_CTL_LO_REG(pll));
+
+ /* Place PLL is the OFF state */
+ mode = readl_relaxed(MODE_REG(pll));
+ mode &= ~PLL_RESET_N;
+ writel_relaxed(mode, MODE_REG(pll));
+
+ /* Place the PLL mode in STANDBY */
+ writel_relaxed(FABIA_PLL_STANDBY, FABIA_PLL_OPMODE(pll));
+}
+
+static void fabia_alpha_pll_disable(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ unsigned long flags;
+
+ spin_lock_irqsave(&alpha_pll_reg_lock, flags);
+ if (pll->fsm_en_mask)
+ __alpha_pll_vote_disable(pll);
+ else
+ __fabia_alpha_pll_disable(pll);
+ spin_unlock_irqrestore(&alpha_pll_reg_lock, flags);
+}
+
+static int fabia_alpha_pll_set_rate(struct clk *c, unsigned long rate)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ unsigned long flags, freq_hz;
+ u32 regval, l_val;
+ u64 a_val;
+
+ freq_hz = round_rate_up(pll, rate, &l_val, &a_val);
+ if (freq_hz > rate + FABIA_RATE_MARGIN || freq_hz < rate) {
+ pr_err("%s: Call clk_set_rate with rounded rates!\n",
+ c->dbg_name);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&c->lock, flags);
+ /* Set the new L value */
+ writel_relaxed(l_val, FABIA_L_REG(pll));
+ writel_relaxed(a_val, FABIA_FRAC_REG(pll));
+
+ /* Latch the input to the PLL */
+ regval = readl_relaxed(MODE_REG(pll));
+ regval |= pll->masks->update_mask;
+ writel_relaxed(regval, MODE_REG(pll));
+
+ /* Wait for 2 reference cycle before checking ACK bit */
+ udelay(1);
+ if (!(readl_relaxed(MODE_REG(pll)) & FABIA_PLL_ACK_LATCH)) {
+ pr_err("%s: PLL latch failed. Leaving PLL disabled\n",
+ c->dbg_name);
+ goto ret;
+ }
+
+ /* Return latch input to 0 */
+ regval = readl_relaxed(MODE_REG(pll));
+ regval &= ~pll->masks->update_mask;
+ writel_relaxed(regval, MODE_REG(pll));
+ret:
+ spin_unlock_irqrestore(&c->lock, flags);
+ return 0;
+}
+
+void __init_fabia_alpha_pll(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ struct alpha_pll_masks *masks = pll->masks;
+ u32 regval;
+
+ if (pll->config_ctl_val)
+ writel_relaxed(pll->config_ctl_val, FABIA_CONFIG_CTL_REG(pll));
+
+ if (masks->output_mask && pll->enable_config) {
+ regval = readl_relaxed(FABIA_USER_CTL_LO_REG(pll));
+ regval &= ~masks->output_mask;
+ regval |= pll->enable_config;
+ writel_relaxed(regval, FABIA_USER_CTL_LO_REG(pll));
+ }
+
+ if (masks->post_div_mask) {
+ regval = readl_relaxed(FABIA_USER_CTL_LO_REG(pll));
+ regval &= ~masks->post_div_mask;
+ regval |= pll->post_div_config;
+ writel_relaxed(regval, FABIA_USER_CTL_LO_REG(pll));
+ }
+
+ if (pll->slew) {
+ regval = readl_relaxed(FABIA_USER_CTL_HI_REG(pll));
+ regval &= ~PLL_LATCH_INTERFACE;
+ writel_relaxed(regval, FABIA_USER_CTL_HI_REG(pll));
+ }
+
+ if (masks->test_ctl_lo_mask) {
+ regval = readl_relaxed(FABIA_TEST_CTL_LO_REG(pll));
+ regval &= ~masks->test_ctl_lo_mask;
+ regval |= pll->test_ctl_lo_val;
+ writel_relaxed(regval, FABIA_TEST_CTL_LO_REG(pll));
+ }
+
+ if (masks->test_ctl_hi_mask) {
+ regval = readl_relaxed(FABIA_TEST_CTL_HI_REG(pll));
+ regval &= ~masks->test_ctl_hi_mask;
+ regval |= pll->test_ctl_hi_val;
+ writel_relaxed(regval, FABIA_TEST_CTL_HI_REG(pll));
+ }
+
+ if (pll->fsm_en_mask)
+ __set_fsm_mode(MODE_REG(pll));
+
+ pll->inited = true;
+}
+
+static enum handoff fabia_alpha_pll_handoff(struct clk *c)
+{
+ struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
+ u64 a_val;
+ u32 l_val, regval;
+
+ /* Set the PLL_HW_UPDATE_LOGIC_BYPASS bit before continuing */
+ regval = readl_relaxed(MODE_REG(pll));
+ regval |= FABIA_PLL_HW_UPDATE_LOGIC_BYPASS;
+ writel_relaxed(regval, MODE_REG(pll));
+
+ if (!is_locked(pll)) {
+ if (c->rate && fabia_alpha_pll_set_rate(c, c->rate))
+ WARN(1, "%s: Failed to configure rate\n", c->dbg_name);
+ __init_alpha_pll(c);
+ return HANDOFF_DISABLED_CLK;
+ } else if (pll->fsm_en_mask && !is_fsm_mode(MODE_REG(pll))) {
+ WARN(1, "%s should be in FSM mode but is not\n", c->dbg_name);
+ }
+
+ l_val = readl_relaxed(FABIA_L_REG(pll));
+ a_val = readl_relaxed(FABIA_FRAC_REG(pll));
+
+ c->rate = compute_rate(pll, l_val, a_val);
+
+ /*
+ * Unconditionally vote for the PLL; it might be on because of
+ * another master's vote.
+ */
+ if (pll->fsm_en_mask)
+ __alpha_pll_vote_enable(pll);
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+struct clk_ops clk_ops_alpha_pll = {
+ .enable = alpha_pll_enable,
+ .disable = alpha_pll_disable,
+ .round_rate = alpha_pll_round_rate,
+ .set_rate = alpha_pll_set_rate,
+ .handoff = alpha_pll_handoff,
+ .list_registers = alpha_pll_list_registers,
+};
+
+struct clk_ops clk_ops_alpha_pll_hwfsm = {
+ .enable = alpha_pll_enable_hwfsm,
+ .disable = alpha_pll_disable_hwfsm,
+ .round_rate = alpha_pll_round_rate,
+ .set_rate = alpha_pll_set_rate,
+ .handoff = alpha_pll_handoff,
+ .list_registers = alpha_pll_list_registers,
+};
+
+struct clk_ops clk_ops_fixed_alpha_pll = {
+ .enable = alpha_pll_enable,
+ .disable = alpha_pll_disable,
+ .handoff = alpha_pll_handoff,
+ .list_registers = alpha_pll_list_registers,
+};
+
+struct clk_ops clk_ops_fixed_fabia_alpha_pll = {
+ .enable = fabia_alpha_pll_enable,
+ .disable = fabia_alpha_pll_disable,
+ .handoff = fabia_alpha_pll_handoff,
+};
+
+struct clk_ops clk_ops_fabia_alpha_pll = {
+ .enable = fabia_alpha_pll_enable,
+ .disable = fabia_alpha_pll_disable,
+ .round_rate = alpha_pll_round_rate,
+ .set_rate = fabia_alpha_pll_set_rate,
+ .handoff = fabia_alpha_pll_handoff,
+};
+
+struct clk_ops clk_ops_dyna_alpha_pll = {
+ .enable = dyna_alpha_pll_enable,
+ .disable = dyna_alpha_pll_disable,
+ .round_rate = alpha_pll_round_rate,
+ .set_rate = dyna_alpha_pll_set_rate,
+ .handoff = alpha_pll_handoff,
+ .list_registers = alpha_pll_list_registers,
+};
+
+static struct alpha_pll_masks masks_20nm_p = {
+ .lock_mask = BIT(31),
+ .active_mask = BIT(30),
+ .vco_mask = BM(21, 20) >> 20,
+ .vco_shift = 20,
+ .alpha_en_mask = BIT(24),
+ .output_mask = 0xF,
+ .post_div_mask = 0xF00,
+};
+
+static struct alpha_pll_vco_tbl vco_20nm_p[] = {
+ VCO(3, 250000000, 500000000),
+ VCO(2, 500000000, 1000000000),
+ VCO(1, 1000000000, 1500000000),
+ VCO(0, 1500000000, 2000000000),
+};
+
+static struct alpha_pll_masks masks_20nm_t = {
+ .lock_mask = BIT(31),
+ .alpha_en_mask = BIT(24),
+ .output_mask = 0xf,
+};
+
+static struct alpha_pll_vco_tbl vco_20nm_t[] = {
+ VCO(0, 500000000, 1250000000),
+};
+
+static struct alpha_pll_clk *alpha_pll_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct alpha_pll_clk *pll;
+ struct msmclk_data *drv;
+
+ pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
+ if (!pll) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (of_property_read_u32(np, "qcom,base-offset", &pll->offset)) {
+ dt_err(np, "missing qcom,base-offset\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Optional property */
+ of_property_read_u32(np, "qcom,post-div-config",
+ &pll->post_div_config);
+
+ pll->masks = devm_kzalloc(dev, sizeof(*pll->masks), GFP_KERNEL);
+ if (!pll->masks) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (of_device_is_compatible(np, "qcom,fixed-alpha-pll-20p") ||
+ of_device_is_compatible(np, "qcom,alpha-pll-20p")) {
+ *pll->masks = masks_20nm_p;
+ pll->vco_tbl = vco_20nm_p;
+ pll->num_vco = ARRAY_SIZE(vco_20nm_p);
+ } else if (of_device_is_compatible(np, "qcom,fixed-alpha-pll-20t") ||
+ of_device_is_compatible(np, "qcom,alpha-pll-20t")) {
+ *pll->masks = masks_20nm_t;
+ pll->vco_tbl = vco_20nm_t;
+ pll->num_vco = ARRAY_SIZE(vco_20nm_t);
+ } else {
+ dt_err(np, "unexpected compatible string\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ drv = msmclk_parse_phandle(dev, np->parent->phandle);
+ if (IS_ERR_OR_NULL(drv))
+ return ERR_CAST(drv);
+ pll->base = &drv->base;
+ return pll;
+}
+
+static void *variable_rate_alpha_pll_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct alpha_pll_clk *pll;
+
+ pll = alpha_pll_dt_parser(dev, np);
+ if (IS_ERR(pll))
+ return pll;
+
+ /* Optional Property */
+ of_property_read_u32(np, "qcom,output-enable", &pll->enable_config);
+
+ pll->c.ops = &clk_ops_alpha_pll;
+ return msmclk_generic_clk_init(dev, np, &pll->c);
+}
+
+static void *fixed_rate_alpha_pll_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct alpha_pll_clk *pll;
+ int rc;
+ u32 val;
+
+ pll = alpha_pll_dt_parser(dev, np);
+ if (IS_ERR(pll))
+ return pll;
+
+ rc = of_property_read_u32(np, "qcom,pll-config-rate", &val);
+ if (rc) {
+ dt_err(np, "missing qcom,pll-config-rate\n");
+ return ERR_PTR(-EINVAL);
+ }
+ pll->c.rate = val;
+
+ rc = of_property_read_u32(np, "qcom,output-enable",
+ &pll->enable_config);
+ if (rc) {
+ dt_err(np, "missing qcom,output-enable\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Optional Property */
+ rc = of_property_read_u32(np, "qcom,fsm-en-bit", &val);
+ if (!rc) {
+ rc = of_property_read_u32(np, "qcom,fsm-en-offset",
+ &pll->fsm_reg_offset);
+ if (rc) {
+ dt_err(np, "missing qcom,fsm-en-offset\n");
+ return ERR_PTR(-EINVAL);
+ }
+ pll->fsm_en_mask = BIT(val);
+ }
+
+ pll->c.ops = &clk_ops_fixed_alpha_pll;
+ return msmclk_generic_clk_init(dev, np, &pll->c);
+}
+
+MSMCLK_PARSER(fixed_rate_alpha_pll_dt_parser, "qcom,fixed-alpha-pll-20p", 0);
+MSMCLK_PARSER(fixed_rate_alpha_pll_dt_parser, "qcom,fixed-alpha-pll-20t", 1);
+MSMCLK_PARSER(variable_rate_alpha_pll_dt_parser, "qcom,alpha-pll-20p", 0);
+MSMCLK_PARSER(variable_rate_alpha_pll_dt_parser, "qcom,alpha-pll-20t", 1);
diff --git a/drivers/clk/msm/clock-debug.c b/drivers/clk/msm/clock-debug.c
new file mode 100644
index 000000000000..d0ff821eb203
--- /dev/null
+++ b/drivers/clk/msm/clock-debug.c
@@ -0,0 +1,676 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/module.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/clkdev.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <trace/events/power.h>
+
+
+#include "clock.h"
+
+static LIST_HEAD(clk_list);
+static DEFINE_MUTEX(clk_list_lock);
+
+static struct dentry *debugfs_base;
+static u32 debug_suspend;
+
+static int clock_debug_rate_set(void *data, u64 val)
+{
+ struct clk *clock = data;
+ int ret;
+
+ /* Only increases to max rate will succeed, but that's actually good
+ * for debugging purposes so we don't check for error. */
+ if (clock->flags & CLKFLAG_MAX)
+ clk_set_max_rate(clock, val);
+ ret = clk_set_rate(clock, val);
+ if (ret)
+ pr_err("clk_set_rate(%s, %lu) failed (%d)\n", clock->dbg_name,
+ (unsigned long)val, ret);
+
+ return ret;
+}
+
+static int clock_debug_rate_get(void *data, u64 *val)
+{
+ struct clk *clock = data;
+ *val = clk_get_rate(clock);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get,
+ clock_debug_rate_set, "%llu\n");
+
+static struct clk *measure;
+
+static int clock_debug_measure_get(void *data, u64 *val)
+{
+ struct clk *clock = data, *par;
+ int ret, is_hw_gated;
+ unsigned long meas_rate, sw_rate;
+
+ /* Check to see if the clock is in hardware gating mode */
+ if (clock->ops->in_hwcg_mode)
+ is_hw_gated = clock->ops->in_hwcg_mode(clock);
+ else
+ is_hw_gated = 0;
+
+ ret = clk_set_parent(measure, clock);
+ if (!ret) {
+ /*
+ * Disable hw gating to get accurate rate measurements. Only do
+ * this if the clock is explictly enabled by software. This
+ * allows us to detect errors where clocks are on even though
+ * software is not requesting them to be on due to broken
+ * hardware gating signals.
+ */
+ if (is_hw_gated && clock->count)
+ clock->ops->disable_hwcg(clock);
+ par = measure;
+ while (par && par != clock) {
+ if (par->ops->enable)
+ par->ops->enable(par);
+ par = par->parent;
+ }
+ *val = clk_get_rate(measure);
+ /* Reenable hwgating if it was disabled */
+ if (is_hw_gated && clock->count)
+ clock->ops->enable_hwcg(clock);
+ }
+
+ /*
+ * If there's a divider on the path from the clock output to the
+ * measurement circuitry, account for it by dividing the original clock
+ * rate with the rate set on the parent of the measure clock.
+ */
+ meas_rate = clk_get_rate(clock);
+ sw_rate = clk_get_rate(measure->parent);
+ if (sw_rate && meas_rate >= (sw_rate * 2))
+ *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate);
+
+ return ret;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_measure_fops, clock_debug_measure_get,
+ NULL, "%lld\n");
+
+static int clock_debug_enable_set(void *data, u64 val)
+{
+ struct clk *clock = data;
+ int rc = 0;
+
+ if (val)
+ rc = clk_prepare_enable(clock);
+ else
+ clk_disable_unprepare(clock);
+
+ return rc;
+}
+
+static int clock_debug_enable_get(void *data, u64 *val)
+{
+ struct clk *clock = data;
+ int enabled;
+
+ if (clock->ops->is_enabled)
+ enabled = clock->ops->is_enabled(clock);
+ else
+ enabled = !!(clock->count);
+
+ *val = enabled;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get,
+ clock_debug_enable_set, "%lld\n");
+
+static int clock_debug_local_get(void *data, u64 *val)
+{
+ struct clk *clock = data;
+
+ if (!clock->ops->is_local)
+ *val = true;
+ else
+ *val = clock->ops->is_local(clock);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_local_fops, clock_debug_local_get,
+ NULL, "%llu\n");
+
+static int clock_debug_hwcg_get(void *data, u64 *val)
+{
+ struct clk *clock = data;
+ if (clock->ops->in_hwcg_mode)
+ *val = !!clock->ops->in_hwcg_mode(clock);
+ else
+ *val = 0;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_hwcg_fops, clock_debug_hwcg_get,
+ NULL, "%llu\n");
+
+static void clock_print_fmax_by_level(struct seq_file *m, int level)
+{
+ struct clk *clock = m->private;
+ struct clk_vdd_class *vdd_class = clock->vdd_class;
+ int off, i, vdd_level, nregs = vdd_class->num_regulators;
+
+ vdd_level = find_vdd_level(clock, clock->rate);
+
+ seq_printf(m, "%2s%10lu", vdd_level == level ? "[" : "",
+ clock->fmax[level]);
+ for (i = 0; i < nregs; i++) {
+ off = nregs*level + i;
+ if (vdd_class->vdd_uv)
+ seq_printf(m, "%10u", vdd_class->vdd_uv[off]);
+ if (vdd_class->vdd_ua)
+ seq_printf(m, "%10u", vdd_class->vdd_ua[off]);
+ }
+
+ if (vdd_level == level)
+ seq_puts(m, "]");
+ seq_puts(m, "\n");
+}
+
+static int fmax_rates_show(struct seq_file *m, void *unused)
+{
+ struct clk *clock = m->private;
+ struct clk_vdd_class *vdd_class = clock->vdd_class;
+ int level = 0, i, nregs = vdd_class->num_regulators;
+ char reg_name[10];
+
+ int vdd_level = find_vdd_level(clock, clock->rate);
+ if (vdd_level < 0) {
+ seq_printf(m, "could not find_vdd_level for %s, %ld\n",
+ clock->dbg_name, clock->rate);
+ return 0;
+ }
+
+ seq_printf(m, "%12s", "");
+ for (i = 0; i < nregs; i++) {
+ snprintf(reg_name, ARRAY_SIZE(reg_name), "reg %d", i);
+ seq_printf(m, "%10s", reg_name);
+ if (vdd_class->vdd_ua)
+ seq_printf(m, "%10s", "");
+ }
+
+ seq_printf(m, "\n%12s", "freq");
+ for (i = 0; i < nregs; i++) {
+ seq_printf(m, "%10s", "uV");
+ if (vdd_class->vdd_ua)
+ seq_printf(m, "%10s", "uA");
+ }
+ seq_printf(m, "\n");
+
+ for (level = 0; level < clock->num_fmax; level++)
+ clock_print_fmax_by_level(m, level);
+
+ return 0;
+}
+
+static int fmax_rates_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fmax_rates_show, inode->i_private);
+}
+
+static const struct file_operations fmax_rates_fops = {
+ .open = fmax_rates_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int orphan_list_show(struct seq_file *m, void *unused)
+{
+ struct clk *c, *safe;
+
+ list_for_each_entry_safe(c, safe, &orphan_clk_list, list)
+ seq_printf(m, "%s\n", c->dbg_name);
+
+ return 0;
+}
+
+static int orphan_list_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, orphan_list_show, inode->i_private);
+}
+
+static const struct file_operations orphan_list_fops = {
+ .open = orphan_list_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+#define clock_debug_output(m, c, fmt, ...) \
+do { \
+ if (m) \
+ seq_printf(m, fmt, ##__VA_ARGS__); \
+ else if (c) \
+ pr_cont(fmt, ##__VA_ARGS__); \
+ else \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
+static int clock_debug_print_clock(struct clk *c, struct seq_file *m)
+{
+ char *start = "";
+
+ if (!c || !c->prepare_count)
+ return 0;
+
+ clock_debug_output(m, 0, "\t");
+ do {
+ if (c->vdd_class)
+ clock_debug_output(m, 1, "%s%s:%u:%u [%ld, %d]", start,
+ c->dbg_name, c->prepare_count, c->count,
+ c->rate, find_vdd_level(c, c->rate));
+ else
+ clock_debug_output(m, 1, "%s%s:%u:%u [%ld]", start,
+ c->dbg_name, c->prepare_count, c->count,
+ c->rate);
+ start = " -> ";
+ } while ((c = clk_get_parent(c)));
+
+ clock_debug_output(m, 1, "\n");
+
+ return 1;
+}
+
+/**
+ * clock_debug_print_enabled_clocks() - Print names of enabled clocks
+ *
+ */
+static void clock_debug_print_enabled_clocks(struct seq_file *m)
+{
+ struct clk *c;
+ int cnt = 0;
+
+ if (!mutex_trylock(&clk_list_lock)) {
+ pr_err("clock-debug: Clocks are being registered. Cannot print clock state now.\n");
+ return;
+ }
+ clock_debug_output(m, 0, "Enabled clocks:\n");
+ list_for_each_entry(c, &clk_list, list) {
+ cnt += clock_debug_print_clock(c, m);
+ }
+ mutex_unlock(&clk_list_lock);
+
+ if (cnt)
+ clock_debug_output(m, 0, "Enabled clock count: %d\n", cnt);
+ else
+ clock_debug_output(m, 0, "No clocks enabled.\n");
+}
+
+static int enabled_clocks_show(struct seq_file *m, void *unused)
+{
+ clock_debug_print_enabled_clocks(m);
+ return 0;
+}
+
+static int enabled_clocks_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, enabled_clocks_show, inode->i_private);
+}
+
+static const struct file_operations enabled_clocks_fops = {
+ .open = enabled_clocks_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int trace_clocks_show(struct seq_file *m, void *unused)
+{
+ struct clk *c;
+ int total_cnt = 0;
+
+ if (!mutex_trylock(&clk_list_lock)) {
+ pr_err("trace_clocks: Clocks are being registered. Cannot trace clock state now.\n");
+ return 1;
+ }
+ list_for_each_entry(c, &clk_list, list) {
+ trace_clock_state(c->dbg_name, c->prepare_count, c->count,
+ c->rate);
+ total_cnt++;
+ }
+ mutex_unlock(&clk_list_lock);
+ clock_debug_output(m, 0, "Total clock count: %d\n", total_cnt);
+
+ return 0;
+}
+
+static int trace_clocks_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, trace_clocks_show, inode->i_private);
+}
+static const struct file_operations trace_clocks_fops = {
+ .open = trace_clocks_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int list_rates_show(struct seq_file *m, void *unused)
+{
+ struct clk *clock = m->private;
+ int level, i = 0;
+ unsigned long rate, fmax = 0;
+
+ /* Find max frequency supported within voltage constraints. */
+ if (!clock->vdd_class) {
+ fmax = ULONG_MAX;
+ } else {
+ for (level = 0; level < clock->num_fmax; level++)
+ if (clock->fmax[level])
+ fmax = clock->fmax[level];
+ }
+
+ /*
+ * List supported frequencies <= fmax. Higher frequencies may appear in
+ * the frequency table, but are not valid and should not be listed.
+ */
+ while (!IS_ERR_VALUE(rate = clock->ops->list_rate(clock, i++))) {
+ if (rate <= fmax)
+ seq_printf(m, "%lu\n", rate);
+ }
+
+ return 0;
+}
+
+static int list_rates_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, list_rates_show, inode->i_private);
+}
+
+static const struct file_operations list_rates_fops = {
+ .open = list_rates_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static ssize_t clock_parent_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct clk *clock = filp->private_data;
+ struct clk *p = clock->parent;
+ char name[256] = {0};
+
+ snprintf(name, sizeof(name), "%s\n", p ? p->dbg_name : "None\n");
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, name, strlen(name));
+}
+
+
+static ssize_t clock_parent_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ struct clk *clock = filp->private_data;
+ char buf[256];
+ char *cmp;
+ int ret;
+ struct clk *parent = NULL;
+
+ cnt = min(cnt, sizeof(buf) - 1);
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+ buf[cnt] = '\0';
+ cmp = strstrip(buf);
+
+ mutex_lock(&clk_list_lock);
+ list_for_each_entry(parent, &clk_list, list) {
+ if (!strcmp(cmp, parent->dbg_name))
+ break;
+ }
+
+ if (&parent->list == &clk_list) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_unlock(&clk_list_lock);
+ ret = clk_set_parent(clock, parent);
+ if (ret)
+ return ret;
+
+ return cnt;
+err:
+ mutex_unlock(&clk_list_lock);
+ return ret;
+}
+
+
+static const struct file_operations clock_parent_fops = {
+ .open = simple_open,
+ .read = clock_parent_read,
+ .write = clock_parent_write,
+};
+
+void clk_debug_print_hw(struct clk *clk, struct seq_file *f)
+{
+ void __iomem *base;
+ struct clk_register_data *regs;
+ u32 i, j, size;
+
+ if (IS_ERR_OR_NULL(clk))
+ return;
+
+ clk_debug_print_hw(clk->parent, f);
+
+ clock_debug_output(f, false, "%s\n", clk->dbg_name);
+
+ if (!clk->ops->list_registers)
+ return;
+
+ j = 0;
+ base = clk->ops->list_registers(clk, j, &regs, &size);
+ while (!IS_ERR(base)) {
+ for (i = 0; i < size; i++) {
+ u32 val = readl_relaxed(base + regs[i].offset);
+ clock_debug_output(f, false, "%20s: 0x%.8x\n",
+ regs[i].name, val);
+ }
+ j++;
+ base = clk->ops->list_registers(clk, j, &regs, &size);
+ }
+}
+
+static int print_hw_show(struct seq_file *m, void *unused)
+{
+ struct clk *c = m->private;
+ clk_debug_print_hw(c, m);
+
+ return 0;
+}
+
+static int print_hw_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, print_hw_show, inode->i_private);
+}
+
+static const struct file_operations clock_print_hw_fops = {
+ .open = print_hw_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+
+static void clock_measure_add(struct clk *clock)
+{
+ if (IS_ERR_OR_NULL(measure))
+ return;
+
+ if (clk_set_parent(measure, clock))
+ return;
+
+ debugfs_create_file("measure", S_IRUGO, clock->clk_dir, clock,
+ &clock_measure_fops);
+}
+
+static int clock_debug_add(struct clk *clock)
+{
+ char temp[50], *ptr;
+ struct dentry *clk_dir;
+
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ strlcpy(temp, clock->dbg_name, ARRAY_SIZE(temp));
+ for (ptr = temp; *ptr; ptr++)
+ *ptr = tolower(*ptr);
+
+ clk_dir = debugfs_create_dir(temp, debugfs_base);
+ if (!clk_dir)
+ return -ENOMEM;
+
+ clock->clk_dir = clk_dir;
+
+ if (!debugfs_create_file("rate", S_IRUGO | S_IWUSR, clk_dir,
+ clock, &clock_rate_fops))
+ goto error;
+
+ if (!debugfs_create_file("enable", S_IRUGO | S_IWUSR, clk_dir,
+ clock, &clock_enable_fops))
+ goto error;
+
+ if (!debugfs_create_file("is_local", S_IRUGO, clk_dir, clock,
+ &clock_local_fops))
+ goto error;
+
+ if (!debugfs_create_file("has_hw_gating", S_IRUGO, clk_dir, clock,
+ &clock_hwcg_fops))
+ goto error;
+
+ if (clock->ops->list_rate)
+ if (!debugfs_create_file("list_rates",
+ S_IRUGO, clk_dir, clock, &list_rates_fops))
+ goto error;
+
+ if (clock->vdd_class && !debugfs_create_file("fmax_rates",
+ S_IRUGO, clk_dir, clock, &fmax_rates_fops))
+ goto error;
+
+ if (!debugfs_create_file("parent", S_IRUGO, clk_dir, clock,
+ &clock_parent_fops))
+ goto error;
+
+ if (!debugfs_create_file("print", S_IRUGO, clk_dir, clock,
+ &clock_print_hw_fops))
+ goto error;
+
+ clock_measure_add(clock);
+
+ return 0;
+error:
+ debugfs_remove_recursive(clk_dir);
+ return -ENOMEM;
+}
+static DEFINE_MUTEX(clk_debug_lock);
+static int clk_debug_init_once;
+
+/**
+ * clock_debug_init() - Initialize clock debugfs
+ * Lock clk_debug_lock before invoking this function.
+ */
+static int clock_debug_init(void)
+{
+ if (clk_debug_init_once)
+ return 0;
+
+ clk_debug_init_once = 1;
+
+ debugfs_base = debugfs_create_dir("clk", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ if (!debugfs_create_u32("debug_suspend", S_IRUGO | S_IWUSR,
+ debugfs_base, &debug_suspend)) {
+ debugfs_remove_recursive(debugfs_base);
+ return -ENOMEM;
+ }
+
+ if (!debugfs_create_file("enabled_clocks", S_IRUGO, debugfs_base, NULL,
+ &enabled_clocks_fops))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("orphan_list", S_IRUGO, debugfs_base, NULL,
+ &orphan_list_fops))
+ return -ENOMEM;
+
+ if (!debugfs_create_file("trace_clocks", S_IRUGO, debugfs_base, NULL,
+ &trace_clocks_fops))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * clock_debug_register() - Add additional clocks to clock debugfs hierarchy
+ * @list: List of clocks to create debugfs nodes for
+ */
+int clock_debug_register(struct clk *clk)
+{
+ int ret = 0;
+ struct clk *c;
+
+ mutex_lock(&clk_list_lock);
+ if (!list_empty(&clk->list))
+ goto out;
+
+ ret = clock_debug_init();
+ if (ret)
+ goto out;
+
+ if (IS_ERR_OR_NULL(measure)) {
+ if (clk->flags & CLKFLAG_MEASURE)
+ measure = clk;
+ if (!IS_ERR_OR_NULL(measure)) {
+ list_for_each_entry(c, &clk_list, list)
+ clock_measure_add(c);
+ }
+ }
+
+ list_add_tail(&clk->list, &clk_list);
+ clock_debug_add(clk);
+out:
+ mutex_unlock(&clk_list_lock);
+ return ret;
+}
+
+/*
+ * Print the names of enabled clocks and their parents if debug_suspend is set
+ */
+void clock_debug_print_enabled(void)
+{
+ if (likely(!debug_suspend))
+ return;
+
+ clock_debug_print_enabled_clocks(NULL);
+}
diff --git a/drivers/clk/msm/clock-dummy.c b/drivers/clk/msm/clock-dummy.c
new file mode 100644
index 000000000000..e5339b110cd6
--- /dev/null
+++ b/drivers/clk/msm/clock-dummy.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 2011,2013-2014 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.
+ */
+
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <soc/qcom/msm-clock-controller.h>
+
+static int dummy_clk_reset(struct clk *clk, enum clk_reset_action action)
+{
+ return 0;
+}
+
+static int dummy_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ clk->rate = rate;
+ return 0;
+}
+
+static int dummy_clk_set_max_rate(struct clk *clk, unsigned long rate)
+{
+ return 0;
+}
+
+static int dummy_clk_set_flags(struct clk *clk, unsigned flags)
+{
+ return 0;
+}
+
+static unsigned long dummy_clk_get_rate(struct clk *clk)
+{
+ return clk->rate;
+}
+
+static long dummy_clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ return rate;
+}
+
+struct clk_ops clk_ops_dummy = {
+ .reset = dummy_clk_reset,
+ .set_rate = dummy_clk_set_rate,
+ .set_max_rate = dummy_clk_set_max_rate,
+ .set_flags = dummy_clk_set_flags,
+ .get_rate = dummy_clk_get_rate,
+ .round_rate = dummy_clk_round_rate,
+};
+
+struct clk dummy_clk = {
+ .dbg_name = "dummy_clk",
+ .ops = &clk_ops_dummy,
+ CLK_INIT(dummy_clk),
+};
+
+static void *dummy_clk_dt_parser(struct device *dev, struct device_node *np)
+{
+ struct clk *c;
+ c = devm_kzalloc(dev, sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ dev_err(dev, "failed to map memory for %s\n", np->name);
+ return ERR_PTR(-ENOMEM);
+ }
+ c->ops = &clk_ops_dummy;
+ return msmclk_generic_clk_init(dev, np, c);
+}
+MSMCLK_PARSER(dummy_clk_dt_parser, "qcom,dummy-clk", 0);
+
+static struct clk *of_dummy_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ return &dummy_clk;
+}
+
+static struct of_device_id msm_clock_dummy_match_table[] = {
+ { .compatible = "qcom,dummycc" },
+ {}
+};
+
+static int msm_clock_dummy_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = of_clk_add_provider(pdev->dev.of_node, of_dummy_get, NULL);
+ if (ret)
+ return -ENOMEM;
+
+ dev_info(&pdev->dev, "Registered DUMMY provider.\n");
+ return ret;
+}
+
+static struct platform_driver msm_clock_dummy_driver = {
+ .probe = msm_clock_dummy_probe,
+ .driver = {
+ .name = "clock-dummy",
+ .of_match_table = msm_clock_dummy_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init msm_dummy_clk_init(void)
+{
+ return platform_driver_register(&msm_clock_dummy_driver);
+}
+arch_initcall(msm_dummy_clk_init);
diff --git a/drivers/clk/msm/clock-generic.c b/drivers/clk/msm/clock-generic.c
new file mode 100644
index 000000000000..dbe378407885
--- /dev/null
+++ b/drivers/clk/msm/clock-generic.c
@@ -0,0 +1,909 @@
+/*
+ * Copyright (c) 2013-2015, 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <soc/qcom/msm-clock-controller.h>
+
+/* ==================== Mux clock ==================== */
+
+static int mux_parent_to_src_sel(struct mux_clk *mux, struct clk *p)
+{
+ return parent_to_src_sel(mux->parents, mux->num_parents, p);
+}
+
+static int mux_set_parent(struct clk *c, struct clk *p)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int sel = mux_parent_to_src_sel(mux, p);
+ struct clk *old_parent;
+ int rc = 0, i;
+ unsigned long flags;
+
+ if (sel < 0 && mux->rec_parents) {
+ for (i = 0; i < mux->num_rec_parents; i++) {
+ rc = clk_set_parent(mux->rec_parents[i], p);
+ if (!rc) {
+ /*
+ * This is necessary to ensure prepare/enable
+ * counts get propagated correctly.
+ */
+ p = mux->rec_parents[i];
+ sel = mux_parent_to_src_sel(mux, p);
+ break;
+ }
+ }
+ }
+
+ if (sel < 0)
+ return sel;
+
+ rc = __clk_pre_reparent(c, p, &flags);
+ if (rc)
+ goto out;
+
+ rc = mux->ops->set_mux_sel(mux, sel);
+ if (rc)
+ goto set_fail;
+
+ old_parent = c->parent;
+ c->parent = p;
+ c->rate = clk_get_rate(p);
+ __clk_post_reparent(c, old_parent, &flags);
+
+ return 0;
+
+set_fail:
+ __clk_post_reparent(c, p, &flags);
+out:
+ return rc;
+}
+
+static long mux_round_rate(struct clk *c, unsigned long rate)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int i;
+ unsigned long prate, rrate = 0;
+
+ for (i = 0; i < mux->num_parents; i++) {
+ prate = clk_round_rate(mux->parents[i].src, rate);
+ if (is_better_rate(rate, rrate, prate))
+ rrate = prate;
+ }
+ if (!rrate)
+ return -EINVAL;
+
+ return rrate;
+}
+
+static int mux_set_rate(struct clk *c, unsigned long rate)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ struct clk *new_parent = NULL;
+ int rc = 0, i;
+ unsigned long new_par_curr_rate;
+ unsigned long flags;
+
+ /*
+ * Check if one of the possible parents is already at the requested
+ * rate.
+ */
+ for (i = 0; i < mux->num_parents && mux->try_get_rate; i++) {
+ struct clk *p = mux->parents[i].src;
+ if (p->rate == rate && clk_round_rate(p, rate) == rate) {
+ new_parent = mux->parents[i].src;
+ break;
+ }
+ }
+
+ for (i = 0; i < mux->num_parents && !(!i && new_parent); i++) {
+ if (clk_round_rate(mux->parents[i].src, rate) == rate) {
+ new_parent = mux->parents[i].src;
+ if (!mux->try_new_parent)
+ break;
+ if (mux->try_new_parent && new_parent != c->parent)
+ break;
+ }
+ }
+
+ if (new_parent == NULL)
+ return -EINVAL;
+
+ /*
+ * Switch to safe parent since the old and new parent might be the
+ * same and the parent might temporarily turn off while switching
+ * rates. If the mux can switch between distinct sources safely
+ * (indicated by try_new_parent), and the new source is not the current
+ * parent, do not switch to the safe parent.
+ */
+ if (mux->safe_sel >= 0 &&
+ !(mux->try_new_parent && (new_parent != c->parent))) {
+ /*
+ * The safe parent might be a clock with multiple sources;
+ * to select the "safe" source, set a safe frequency.
+ */
+ if (mux->safe_freq) {
+ rc = clk_set_rate(mux->safe_parent, mux->safe_freq);
+ if (rc) {
+ pr_err("Failed to set safe rate on %s\n",
+ clk_name(mux->safe_parent));
+ return rc;
+ }
+ }
+
+ /*
+ * Some mux implementations might switch to/from a low power
+ * parent as part of their disable/enable ops. Grab the
+ * enable lock to avoid racing with these implementations.
+ */
+ spin_lock_irqsave(&c->lock, flags);
+ rc = mux->ops->set_mux_sel(mux, mux->safe_sel);
+ spin_unlock_irqrestore(&c->lock, flags);
+ if (rc)
+ return rc;
+
+ }
+
+ new_par_curr_rate = clk_get_rate(new_parent);
+ rc = clk_set_rate(new_parent, rate);
+ if (rc)
+ goto set_rate_fail;
+
+ rc = mux_set_parent(c, new_parent);
+ if (rc)
+ goto set_par_fail;
+
+ return 0;
+
+set_par_fail:
+ clk_set_rate(new_parent, new_par_curr_rate);
+set_rate_fail:
+ WARN(mux->ops->set_mux_sel(mux,
+ mux_parent_to_src_sel(mux, c->parent)),
+ "Set rate failed for %s. Also in bad state!\n", c->dbg_name);
+ return rc;
+}
+
+static int mux_enable(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ if (mux->ops->enable)
+ return mux->ops->enable(mux);
+ return 0;
+}
+
+static void mux_disable(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ if (mux->ops->disable)
+ return mux->ops->disable(mux);
+}
+
+static struct clk *mux_get_parent(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int sel = mux->ops->get_mux_sel(mux);
+ int i;
+
+ for (i = 0; i < mux->num_parents; i++) {
+ if (mux->parents[i].sel == sel)
+ return mux->parents[i].src;
+ }
+
+ /* Unfamiliar parent. */
+ return NULL;
+}
+
+static enum handoff mux_handoff(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+
+ c->rate = clk_get_rate(c->parent);
+ mux->safe_sel = mux_parent_to_src_sel(mux, mux->safe_parent);
+
+ if (mux->en_mask && mux->ops && mux->ops->is_enabled)
+ return mux->ops->is_enabled(mux)
+ ? HANDOFF_ENABLED_CLK
+ : HANDOFF_DISABLED_CLK;
+
+ /*
+ * If this function returns 'enabled' even when the clock downstream
+ * of this clock is disabled, then handoff code will unnecessarily
+ * enable the current parent of this clock. If this function always
+ * returns 'disabled' and a clock downstream is on, the clock handoff
+ * code will bump up the ref count for this clock and its current
+ * parent as necessary. So, clocks without an actual HW gate can
+ * always return disabled.
+ */
+ return HANDOFF_DISABLED_CLK;
+}
+
+static void __iomem *mux_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+
+ if (mux->ops && mux->ops->list_registers)
+ return mux->ops->list_registers(mux, n, regs, size);
+
+ return ERR_PTR(-EINVAL);
+}
+
+struct clk_ops clk_ops_gen_mux = {
+ .enable = mux_enable,
+ .disable = mux_disable,
+ .set_parent = mux_set_parent,
+ .round_rate = mux_round_rate,
+ .set_rate = mux_set_rate,
+ .handoff = mux_handoff,
+ .get_parent = mux_get_parent,
+ .list_registers = mux_clk_list_registers,
+};
+
+/* ==================== Divider clock ==================== */
+
+static long __div_round_rate(struct div_data *data, unsigned long rate,
+ struct clk *parent, unsigned int *best_div, unsigned long *best_prate)
+{
+ unsigned int div, min_div, max_div, _best_div = 1;
+ unsigned long prate, _best_prate = 0, rrate = 0, req_prate, actual_rate;
+ unsigned int numer;
+
+ rate = max(rate, 1UL);
+
+ min_div = max(data->min_div, 1U);
+ max_div = min(data->max_div, (unsigned int) (ULONG_MAX / rate));
+
+ /*
+ * div values are doubled for half dividers.
+ * Adjust for that by picking a numer of 2.
+ */
+ numer = data->is_half_divider ? 2 : 1;
+
+ for (div = min_div; div <= max_div; div++) {
+ if (data->skip_odd_div && (div & 1))
+ if (!(data->allow_div_one && (div == 1)))
+ continue;
+ if (data->skip_even_div && !(div & 1))
+ continue;
+ req_prate = mult_frac(rate, div, numer);
+ prate = clk_round_rate(parent, req_prate);
+ if (IS_ERR_VALUE(prate))
+ break;
+
+ actual_rate = mult_frac(prate, numer, div);
+ if (is_better_rate(rate, rrate, actual_rate)) {
+ rrate = actual_rate;
+ _best_div = div;
+ _best_prate = prate;
+ }
+
+ /*
+ * Trying higher dividers is only going to ask the parent for
+ * a higher rate. If it can't even output a rate higher than
+ * the one we request for this divider, the parent is not
+ * going to be able to output an even higher rate required
+ * for a higher divider. So, stop trying higher dividers.
+ */
+ if (actual_rate < rate)
+ break;
+
+ if (rrate <= rate + data->rate_margin)
+ break;
+ }
+
+ if (!rrate)
+ return -EINVAL;
+ if (best_div)
+ *best_div = _best_div;
+ if (best_prate)
+ *best_prate = _best_prate;
+
+ return rrate;
+}
+
+static long div_round_rate(struct clk *c, unsigned long rate)
+{
+ struct div_clk *d = to_div_clk(c);
+
+ return __div_round_rate(&d->data, rate, c->parent, NULL, NULL);
+}
+
+static int _find_safe_div(struct clk *c, unsigned long rate)
+{
+ struct div_clk *d = to_div_clk(c);
+ struct div_data *data = &d->data;
+ unsigned long fast = max(rate, c->rate);
+ unsigned int numer = data->is_half_divider ? 2 : 1;
+ int i, safe_div = 0;
+
+ if (!d->safe_freq)
+ return 0;
+
+ /* Find the max safe freq that is lesser than fast */
+ for (i = data->max_div; i >= data->min_div; i--)
+ if (mult_frac(d->safe_freq, numer, i) <= fast)
+ safe_div = i;
+
+ return safe_div ?: -EINVAL;
+}
+
+static int div_set_rate(struct clk *c, unsigned long rate)
+{
+ struct div_clk *d = to_div_clk(c);
+ int safe_div, div, rc = 0;
+ long rrate, old_prate, new_prate;
+ struct div_data *data = &d->data;
+
+ rrate = __div_round_rate(data, rate, c->parent, &div, &new_prate);
+ if (rrate < rate || rrate > rate + data->rate_margin)
+ return -EINVAL;
+
+ /*
+ * For fixed divider clock we don't want to return an error if the
+ * requested rate matches the achievable rate. So, don't check for
+ * !d->ops and return an error. __div_round_rate() ensures div ==
+ * d->div if !d->ops.
+ */
+
+ safe_div = _find_safe_div(c, rate);
+ if (d->safe_freq && safe_div < 0) {
+ pr_err("No safe div on %s for transitioning from %lu to %lu\n",
+ c->dbg_name, c->rate, rate);
+ return -EINVAL;
+ }
+
+ safe_div = max(safe_div, div);
+
+ if (safe_div > data->div) {
+ rc = d->ops->set_div(d, safe_div);
+ if (rc) {
+ pr_err("Failed to set div %d on %s\n", safe_div,
+ c->dbg_name);
+ return rc;
+ }
+ }
+
+ old_prate = clk_get_rate(c->parent);
+ rc = clk_set_rate(c->parent, new_prate);
+ if (rc)
+ goto set_rate_fail;
+
+ if (div < data->div)
+ rc = d->ops->set_div(d, div);
+ else if (div < safe_div)
+ rc = d->ops->set_div(d, div);
+ if (rc)
+ goto div_dec_fail;
+
+ data->div = div;
+
+ return 0;
+
+div_dec_fail:
+ WARN(clk_set_rate(c->parent, old_prate),
+ "Set rate failed for %s. Also in bad state!\n", c->dbg_name);
+set_rate_fail:
+ if (safe_div > data->div)
+ WARN(d->ops->set_div(d, data->div),
+ "Set rate failed for %s. Also in bad state!\n",
+ c->dbg_name);
+ return rc;
+}
+
+static int div_enable(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ if (d->ops && d->ops->enable)
+ return d->ops->enable(d);
+ return 0;
+}
+
+static void div_disable(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ if (d->ops && d->ops->disable)
+ return d->ops->disable(d);
+}
+
+static enum handoff div_handoff(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ unsigned int div = d->data.div;
+
+ if (d->ops && d->ops->get_div)
+ div = max(d->ops->get_div(d), 1);
+ div = max(div, 1U);
+ c->rate = clk_get_rate(c->parent) / div;
+
+ if (!d->ops || !d->ops->set_div)
+ d->data.min_div = d->data.max_div = div;
+ d->data.div = div;
+
+ if (d->en_mask && d->ops && d->ops->is_enabled)
+ return d->ops->is_enabled(d)
+ ? HANDOFF_ENABLED_CLK
+ : HANDOFF_DISABLED_CLK;
+
+ /*
+ * If this function returns 'enabled' even when the clock downstream
+ * of this clock is disabled, then handoff code will unnecessarily
+ * enable the current parent of this clock. If this function always
+ * returns 'disabled' and a clock downstream is on, the clock handoff
+ * code will bump up the ref count for this clock and its current
+ * parent as necessary. So, clocks without an actual HW gate can
+ * always return disabled.
+ */
+ return HANDOFF_DISABLED_CLK;
+}
+
+static void __iomem *div_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct div_clk *d = to_div_clk(c);
+
+ if (d->ops && d->ops->list_registers)
+ return d->ops->list_registers(d, n, regs, size);
+
+ return ERR_PTR(-EINVAL);
+}
+
+struct clk_ops clk_ops_div = {
+ .enable = div_enable,
+ .disable = div_disable,
+ .round_rate = div_round_rate,
+ .set_rate = div_set_rate,
+ .handoff = div_handoff,
+ .list_registers = div_clk_list_registers,
+};
+
+static long __slave_div_round_rate(struct clk *c, unsigned long rate,
+ int *best_div)
+{
+ struct div_clk *d = to_div_clk(c);
+ unsigned int div, min_div, max_div;
+ long p_rate;
+
+ rate = max(rate, 1UL);
+
+ min_div = d->data.min_div;
+ max_div = d->data.max_div;
+
+ p_rate = clk_get_rate(c->parent);
+ div = DIV_ROUND_CLOSEST(p_rate, rate);
+ div = max(div, min_div);
+ div = min(div, max_div);
+ if (best_div)
+ *best_div = div;
+
+ return p_rate / div;
+}
+
+static long slave_div_round_rate(struct clk *c, unsigned long rate)
+{
+ return __slave_div_round_rate(c, rate, NULL);
+}
+
+static int slave_div_set_rate(struct clk *c, unsigned long rate)
+{
+ struct div_clk *d = to_div_clk(c);
+ int div, rc = 0;
+ long rrate;
+
+ rrate = __slave_div_round_rate(c, rate, &div);
+ if (rrate != rate)
+ return -EINVAL;
+
+ if (div == d->data.div)
+ return 0;
+
+ /*
+ * For fixed divider clock we don't want to return an error if the
+ * requested rate matches the achievable rate. So, don't check for
+ * !d->ops and return an error. __slave_div_round_rate() ensures
+ * div == d->data.div if !d->ops.
+ */
+ rc = d->ops->set_div(d, div);
+ if (rc)
+ return rc;
+
+ d->data.div = div;
+
+ return 0;
+}
+
+static unsigned long slave_div_get_rate(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ if (!d->data.div)
+ return 0;
+ return clk_get_rate(c->parent) / d->data.div;
+}
+
+struct clk_ops clk_ops_slave_div = {
+ .enable = div_enable,
+ .disable = div_disable,
+ .round_rate = slave_div_round_rate,
+ .set_rate = slave_div_set_rate,
+ .get_rate = slave_div_get_rate,
+ .handoff = div_handoff,
+ .list_registers = div_clk_list_registers,
+};
+
+
+/**
+ * External clock
+ * Some clock controllers have input clock signal that come from outside the
+ * clock controller. That input clock signal might then be used as a source for
+ * several clocks inside the clock controller. This external clock
+ * implementation models this input clock signal by just passing on the requests
+ * to the clock's parent, the original external clock source. The driver for the
+ * clock controller should clk_get() the original external clock in the probe
+ * function and set is as a parent to this external clock..
+ */
+
+long parent_round_rate(struct clk *c, unsigned long rate)
+{
+ return clk_round_rate(c->parent, rate);
+}
+
+int parent_set_rate(struct clk *c, unsigned long rate)
+{
+ return clk_set_rate(c->parent, rate);
+}
+
+unsigned long parent_get_rate(struct clk *c)
+{
+ return clk_get_rate(c->parent);
+}
+
+static int ext_set_parent(struct clk *c, struct clk *p)
+{
+ return clk_set_parent(c->parent, p);
+}
+
+static struct clk *ext_get_parent(struct clk *c)
+{
+ struct ext_clk *ext = to_ext_clk(c);
+
+ if (!IS_ERR_OR_NULL(c->parent))
+ return c->parent;
+ return clk_get(ext->dev, ext->clk_id);
+}
+
+static enum handoff ext_handoff(struct clk *c)
+{
+ c->rate = clk_get_rate(c->parent);
+ /* Similar reasoning applied in div_handoff, see comment there. */
+ return HANDOFF_DISABLED_CLK;
+}
+
+struct clk_ops clk_ops_ext = {
+ .handoff = ext_handoff,
+ .round_rate = parent_round_rate,
+ .set_rate = parent_set_rate,
+ .get_rate = parent_get_rate,
+ .set_parent = ext_set_parent,
+ .get_parent = ext_get_parent,
+};
+
+static void *ext_clk_dt_parser(struct device *dev, struct device_node *np)
+{
+ struct ext_clk *ext;
+ const char *str;
+ int rc;
+
+ ext = devm_kzalloc(dev, sizeof(*ext), GFP_KERNEL);
+ if (!ext) {
+ dev_err(dev, "memory allocation failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ext->dev = dev;
+ rc = of_property_read_string(np, "qcom,clock-names", &str);
+ if (!rc)
+ ext->clk_id = (void *)str;
+
+ ext->c.ops = &clk_ops_ext;
+ return msmclk_generic_clk_init(dev, np, &ext->c);
+}
+MSMCLK_PARSER(ext_clk_dt_parser, "qcom,ext-clk", 0);
+
+/* ==================== Mux_div clock ==================== */
+
+static int mux_div_clk_enable(struct clk *c)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+
+ if (md->ops->enable)
+ return md->ops->enable(md);
+ return 0;
+}
+
+static void mux_div_clk_disable(struct clk *c)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+
+ if (md->ops->disable)
+ return md->ops->disable(md);
+}
+
+static long __mux_div_round_rate(struct clk *c, unsigned long rate,
+ struct clk **best_parent, int *best_div, unsigned long *best_prate)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+ unsigned int i;
+ unsigned long rrate, best = 0, _best_div = 0, _best_prate = 0;
+ struct clk *_best_parent = 0;
+
+ if (md->try_get_rate) {
+ for (i = 0; i < md->num_parents; i++) {
+ int divider;
+ unsigned long p_rate;
+
+ rrate = __div_round_rate(&md->data, rate,
+ md->parents[i].src,
+ &divider, &p_rate);
+ /*
+ * Check if one of the possible parents is already at
+ * the requested rate.
+ */
+ if (p_rate == clk_get_rate(md->parents[i].src)
+ && rrate == rate) {
+ best = rrate;
+ _best_div = divider;
+ _best_prate = p_rate;
+ _best_parent = md->parents[i].src;
+ goto end;
+ }
+ }
+ }
+
+ for (i = 0; i < md->num_parents; i++) {
+ int div;
+ unsigned long prate;
+
+ rrate = __div_round_rate(&md->data, rate, md->parents[i].src,
+ &div, &prate);
+
+ if (is_better_rate(rate, best, rrate)) {
+ best = rrate;
+ _best_div = div;
+ _best_prate = prate;
+ _best_parent = md->parents[i].src;
+ }
+
+ if (rate <= rrate && rrate <= rate + md->data.rate_margin)
+ break;
+ }
+end:
+ if (best_div)
+ *best_div = _best_div;
+ if (best_prate)
+ *best_prate = _best_prate;
+ if (best_parent)
+ *best_parent = _best_parent;
+
+ if (best)
+ return best;
+ return -EINVAL;
+}
+
+static long mux_div_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ return __mux_div_round_rate(c, rate, NULL, NULL, NULL);
+}
+
+/* requires enable lock to be held */
+static int __set_src_div(struct mux_div_clk *md, struct clk *parent, u32 div)
+{
+ u32 rc = 0, src_sel;
+
+ src_sel = parent_to_src_sel(md->parents, md->num_parents, parent);
+ /*
+ * If the clock is disabled, don't change to the new settings until
+ * the clock is reenabled
+ */
+ if (md->c.count)
+ rc = md->ops->set_src_div(md, src_sel, div);
+ if (!rc) {
+ md->data.div = div;
+ md->src_sel = src_sel;
+ }
+
+ return rc;
+}
+
+static int set_src_div(struct mux_div_clk *md, struct clk *parent, u32 div)
+{
+ unsigned long flags;
+ u32 rc;
+
+ spin_lock_irqsave(&md->c.lock, flags);
+ rc = __set_src_div(md, parent, div);
+ spin_unlock_irqrestore(&md->c.lock, flags);
+
+ return rc;
+}
+
+/* Must be called after handoff to ensure parent clock rates are initialized */
+static int safe_parent_init_once(struct clk *c)
+{
+ unsigned long rrate;
+ u32 best_div;
+ struct clk *best_parent;
+ struct mux_div_clk *md = to_mux_div_clk(c);
+
+ if (IS_ERR(md->safe_parent))
+ return -EINVAL;
+ if (!md->safe_freq || md->safe_parent)
+ return 0;
+
+ rrate = __mux_div_round_rate(c, md->safe_freq, &best_parent,
+ &best_div, NULL);
+
+ if (rrate == md->safe_freq) {
+ md->safe_div = best_div;
+ md->safe_parent = best_parent;
+ } else {
+ md->safe_parent = ERR_PTR(-EINVAL);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mux_div_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+ unsigned long flags, rrate;
+ unsigned long new_prate, new_parent_orig_rate;
+ struct clk *old_parent, *new_parent;
+ u32 new_div, old_div;
+ int rc;
+
+ rc = safe_parent_init_once(c);
+ if (rc)
+ return rc;
+
+ rrate = __mux_div_round_rate(c, rate, &new_parent, &new_div,
+ &new_prate);
+ if (rrate < rate || rrate > rate + md->data.rate_margin)
+ return -EINVAL;
+
+ old_parent = c->parent;
+ old_div = md->data.div;
+
+ /* Refer to the description of safe_freq in clock-generic.h */
+ if (md->safe_freq)
+ rc = set_src_div(md, md->safe_parent, md->safe_div);
+
+ else if (new_parent == old_parent && new_div >= old_div) {
+ /*
+ * If both the parent_rate and divider changes, there may be an
+ * intermediate frequency generated. Ensure this intermediate
+ * frequency is less than both the new rate and previous rate.
+ */
+ rc = set_src_div(md, old_parent, new_div);
+ }
+ if (rc)
+ return rc;
+
+ new_parent_orig_rate = clk_get_rate(new_parent);
+ rc = clk_set_rate(new_parent, new_prate);
+ if (rc) {
+ pr_err("failed to set %s to %ld\n",
+ clk_name(new_parent), new_prate);
+ goto err_set_rate;
+ }
+
+ rc = __clk_pre_reparent(c, new_parent, &flags);
+ if (rc)
+ goto err_pre_reparent;
+
+ /* Set divider and mux src atomically */
+ rc = __set_src_div(md, new_parent, new_div);
+ if (rc)
+ goto err_set_src_div;
+
+ c->parent = new_parent;
+
+ __clk_post_reparent(c, old_parent, &flags);
+ return 0;
+
+err_set_src_div:
+ /* Not switching to new_parent, so disable it */
+ __clk_post_reparent(c, new_parent, &flags);
+err_pre_reparent:
+ rc = clk_set_rate(new_parent, new_parent_orig_rate);
+ WARN(rc, "%s: error changing new_parent (%s) rate back to %ld\n",
+ clk_name(c), clk_name(new_parent), new_parent_orig_rate);
+err_set_rate:
+ rc = set_src_div(md, old_parent, old_div);
+ WARN(rc, "%s: error changing back to original div (%d) and parent (%s)\n",
+ clk_name(c), old_div, clk_name(old_parent));
+
+ return rc;
+}
+
+static struct clk *mux_div_clk_get_parent(struct clk *c)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+ u32 i, div, src_sel;
+
+ md->ops->get_src_div(md, &src_sel, &div);
+
+ md->data.div = div;
+ md->src_sel = src_sel;
+
+ for (i = 0; i < md->num_parents; i++) {
+ if (md->parents[i].sel == src_sel)
+ return md->parents[i].src;
+ }
+
+ return NULL;
+}
+
+static enum handoff mux_div_clk_handoff(struct clk *c)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+ unsigned long parent_rate;
+ unsigned int numer;
+
+ parent_rate = clk_get_rate(c->parent);
+ if (!parent_rate)
+ return HANDOFF_DISABLED_CLK;
+ /*
+ * div values are doubled for half dividers.
+ * Adjust for that by picking a numer of 2.
+ */
+ numer = md->data.is_half_divider ? 2 : 1;
+
+ if (md->data.div) {
+ c->rate = mult_frac(parent_rate, numer, md->data.div);
+ } else {
+ c->rate = 0;
+ return HANDOFF_DISABLED_CLK;
+ }
+
+ if (!md->ops->is_enabled)
+ return HANDOFF_DISABLED_CLK;
+ if (md->ops->is_enabled(md))
+ return HANDOFF_ENABLED_CLK;
+ return HANDOFF_DISABLED_CLK;
+}
+
+static void __iomem *mux_div_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+
+ if (md->ops && md->ops->list_registers)
+ return md->ops->list_registers(md, n , regs, size);
+
+ return ERR_PTR(-EINVAL);
+}
+
+struct clk_ops clk_ops_mux_div_clk = {
+ .enable = mux_div_clk_enable,
+ .disable = mux_div_clk_disable,
+ .set_rate = mux_div_clk_set_rate,
+ .round_rate = mux_div_clk_round_rate,
+ .get_parent = mux_div_clk_get_parent,
+ .handoff = mux_div_clk_handoff,
+ .list_registers = mux_div_clk_list_registers,
+};
diff --git a/drivers/clk/msm/clock-local2.c b/drivers/clk/msm/clock-local2.c
new file mode 100644
index 000000000000..6744330b8657
--- /dev/null
+++ b/drivers/clk/msm/clock-local2.c
@@ -0,0 +1,2682 @@
+/* Copyright (c) 2012-2015, 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/init.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/clk/msm-clock-generic.h>
+#include <soc/qcom/clock-local2.h>
+#include <soc/qcom/msm-clock-controller.h>
+
+/*
+ * When enabling/disabling a clock, check the halt bit up to this number
+ * number of times (with a 1 us delay in between) before continuing.
+ */
+#define HALT_CHECK_MAX_LOOPS 500
+/* For clock without halt checking, wait this long after enables/disables. */
+#define HALT_CHECK_DELAY_US 500
+
+/*
+ * When updating an RCG configuration, check the update bit up to this number
+ * number of times (with a 1 us delay in between) before continuing.
+ */
+#define UPDATE_CHECK_MAX_LOOPS 500
+
+DEFINE_SPINLOCK(local_clock_reg_lock);
+struct clk_freq_tbl rcg_dummy_freq = F_END;
+
+#define CMD_RCGR_REG(x) (*(x)->base + (x)->cmd_rcgr_reg)
+#define CFG_RCGR_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x4)
+#define M_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x8)
+#define N_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0xC)
+#define D_REG(x) (*(x)->base + (x)->cmd_rcgr_reg + 0x10)
+#define CBCR_REG(x) (*(x)->base + (x)->cbcr_reg)
+#define BCR_REG(x) (*(x)->base + (x)->bcr_reg)
+#define RST_REG(x) (*(x)->base + (x)->reset_reg)
+#define VOTE_REG(x) (*(x)->base + (x)->vote_reg)
+#define GATE_EN_REG(x) (*(x)->base + (x)->en_reg)
+#define DIV_REG(x) (*(x)->base + (x)->offset)
+#define MUX_REG(x) (*(x)->base + (x)->offset)
+
+/*
+ * Important clock bit positions and masks
+ */
+#define CMD_RCGR_ROOT_ENABLE_BIT BIT(1)
+#define CBCR_BRANCH_ENABLE_BIT BIT(0)
+#define CBCR_BRANCH_OFF_BIT BIT(31)
+#define CMD_RCGR_CONFIG_UPDATE_BIT BIT(0)
+#define CMD_RCGR_ROOT_STATUS_BIT BIT(31)
+#define BCR_BLK_ARES_BIT BIT(0)
+#define CBCR_HW_CTL_BIT BIT(1)
+#define CFG_RCGR_DIV_MASK BM(4, 0)
+#define CFG_RCGR_SRC_SEL_MASK BM(10, 8)
+#define MND_MODE_MASK BM(13, 12)
+#define MND_DUAL_EDGE_MODE_BVAL BVAL(13, 12, 0x2)
+#define CMD_RCGR_CONFIG_DIRTY_MASK BM(7, 4)
+#define CBCR_CDIV_LSB 16
+#define CBCR_CDIV_MSB 19
+
+enum branch_state {
+ BRANCH_ON,
+ BRANCH_OFF,
+};
+
+static struct clk_freq_tbl cxo_f = {
+ .freq_hz = 19200000,
+ .m_val = 0,
+ .n_val = 0,
+ .d_val = 0,
+ .div_src_val = 0,
+};
+
+struct div_map {
+ u32 mask;
+ int div;
+};
+
+/*
+ * RCG functions
+ */
+
+/*
+ * Update an RCG with a new configuration. This may include a new M, N, or D
+ * value, source selection or pre-divider value.
+ *
+ */
+static void rcg_update_config(struct rcg_clk *rcg)
+{
+ u32 cmd_rcgr_regval, count;
+
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
+ cmd_rcgr_regval |= CMD_RCGR_CONFIG_UPDATE_BIT;
+ writel_relaxed(cmd_rcgr_regval, CMD_RCGR_REG(rcg));
+
+ /* Wait for update to take effect */
+ for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) {
+ if (!(readl_relaxed(CMD_RCGR_REG(rcg)) &
+ CMD_RCGR_CONFIG_UPDATE_BIT))
+ return;
+ udelay(1);
+ }
+
+ CLK_WARN(&rcg->c, count == 0, "rcg didn't update its configuration.");
+}
+
+static void rcg_on_check(struct rcg_clk *rcg)
+{
+ int count;
+
+ /* Wait for RCG to turn on */
+ for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) {
+ if (!(readl_relaxed(CMD_RCGR_REG(rcg)) &
+ CMD_RCGR_ROOT_STATUS_BIT))
+ return;
+ udelay(1);
+ }
+ CLK_WARN(&rcg->c, count == 0, "rcg didn't turn on.");
+}
+
+/* RCG set rate function for clocks with Half Integer Dividers. */
+static void __set_rate_hid(struct rcg_clk *rcg, struct clk_freq_tbl *nf)
+{
+ u32 cfg_regval;
+
+ cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg));
+ cfg_regval &= ~(CFG_RCGR_DIV_MASK | CFG_RCGR_SRC_SEL_MASK);
+ cfg_regval |= nf->div_src_val;
+ writel_relaxed(cfg_regval, CFG_RCGR_REG(rcg));
+
+ rcg_update_config(rcg);
+}
+
+void set_rate_hid(struct rcg_clk *rcg, struct clk_freq_tbl *nf)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ __set_rate_hid(rcg, nf);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+/* RCG set rate function for clocks with MND & Half Integer Dividers. */
+static void __set_rate_mnd(struct rcg_clk *rcg, struct clk_freq_tbl *nf)
+{
+ u32 cfg_regval;
+
+ cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg));
+ writel_relaxed(nf->m_val, M_REG(rcg));
+ writel_relaxed(nf->n_val, N_REG(rcg));
+ writel_relaxed(nf->d_val, D_REG(rcg));
+
+ cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg));
+ cfg_regval &= ~(CFG_RCGR_DIV_MASK | CFG_RCGR_SRC_SEL_MASK);
+ cfg_regval |= nf->div_src_val;
+
+ /* Activate or disable the M/N:D divider as necessary */
+ cfg_regval &= ~MND_MODE_MASK;
+ if (nf->n_val != 0)
+ cfg_regval |= MND_DUAL_EDGE_MODE_BVAL;
+ writel_relaxed(cfg_regval, CFG_RCGR_REG(rcg));
+
+ rcg_update_config(rcg);
+}
+
+void set_rate_mnd(struct rcg_clk *rcg, struct clk_freq_tbl *nf)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ __set_rate_mnd(rcg, nf);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static void rcg_set_force_enable(struct rcg_clk *rcg)
+{
+ u32 cmd_rcgr_regval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
+ cmd_rcgr_regval |= CMD_RCGR_ROOT_ENABLE_BIT;
+ writel_relaxed(cmd_rcgr_regval, CMD_RCGR_REG(rcg));
+ rcg_on_check(rcg);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static void rcg_clear_force_enable(struct rcg_clk *rcg)
+{
+ u32 cmd_rcgr_regval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
+ cmd_rcgr_regval &= ~CMD_RCGR_ROOT_ENABLE_BIT;
+ writel_relaxed(cmd_rcgr_regval, CMD_RCGR_REG(rcg));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static int rcg_clk_enable(struct clk *c)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+
+ WARN(rcg->current_freq == &rcg_dummy_freq,
+ "Attempting to prepare %s before setting its rate. "
+ "Set the rate first!\n", rcg->c.dbg_name);
+
+ if (rcg->force_enable_rcgr) {
+ rcg_set_force_enable(rcg);
+ return 0;
+ }
+
+ if (!rcg->non_local_children || rcg->current_freq == &rcg_dummy_freq)
+ return 0;
+ /*
+ * Switch from CXO to saved mux value. Force enable/disable while
+ * switching. The current parent is already prepared and enabled
+ * at this point, and the CXO source is always-on. Therefore the
+ * RCG can safely execute a dynamic switch.
+ */
+ rcg_set_force_enable(rcg);
+ rcg->set_rate(rcg, rcg->current_freq);
+ rcg_clear_force_enable(rcg);
+
+ return 0;
+}
+
+static void rcg_clk_disable(struct clk *c)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+
+ if (rcg->force_enable_rcgr) {
+ rcg_clear_force_enable(rcg);
+ return;
+ }
+
+ if (!rcg->non_local_children)
+ return;
+
+ /*
+ * Save mux select and switch to CXO. Force enable/disable while
+ * switching. The current parent is still prepared and enabled at this
+ * point, and the CXO source is always-on. Therefore the RCG can safely
+ * execute a dynamic switch.
+ */
+ rcg_set_force_enable(rcg);
+ rcg->set_rate(rcg, &cxo_f);
+ rcg_clear_force_enable(rcg);
+}
+
+static int rcg_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ struct clk_freq_tbl *cf, *nf;
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ int rc;
+ unsigned long flags;
+
+ for (nf = rcg->freq_tbl; nf->freq_hz != FREQ_END
+ && nf->freq_hz != rate; nf++)
+ ;
+
+ if (nf->freq_hz == FREQ_END)
+ return -EINVAL;
+
+ cf = rcg->current_freq;
+ if (nf->src_freq != FIXED_CLK_SRC) {
+ rc = clk_set_rate(nf->src_clk, nf->src_freq);
+ if (rc)
+ return rc;
+ }
+
+ rc = __clk_pre_reparent(c, nf->src_clk, &flags);
+ if (rc)
+ return rc;
+
+ BUG_ON(!rcg->set_rate);
+
+ /*
+ * Perform clock-specific frequency switch operations.
+ *
+ * For RCGs with non_local_children set to true:
+ * If this RCG has at least one branch that is controlled by another
+ * execution entity, ensure that the enable/disable and mux switch
+ * are staggered.
+ */
+ if (!rcg->non_local_children) {
+ rcg->set_rate(rcg, nf);
+ } else if (c->count) {
+ /*
+ * Force enable the RCG here since there could be a disable
+ * call happening between pre_reparent and set_rate.
+ */
+ rcg_set_force_enable(rcg);
+ rcg->set_rate(rcg, nf);
+ rcg_clear_force_enable(rcg);
+ }
+ /*
+ * If non_local_children is set and the RCG is not enabled,
+ * the following operations switch parent in software and cache
+ * the frequency. The mux switch will occur when the RCG is enabled.
+ */
+ rcg->current_freq = nf;
+ c->parent = nf->src_clk;
+
+ __clk_post_reparent(c, cf->src_clk, &flags);
+
+ return 0;
+}
+
+/*
+ * Return a supported rate that's at least the specified rate or
+ * the max supported rate if the specified rate is larger than the
+ * max supported rate.
+ */
+static long rcg_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ struct clk_freq_tbl *f;
+
+ for (f = rcg->freq_tbl; f->freq_hz != FREQ_END; f++)
+ if (f->freq_hz >= rate)
+ return f->freq_hz;
+
+ f--;
+ return f->freq_hz;
+}
+
+/* Return the nth supported frequency for a given clock. */
+static long rcg_clk_list_rate(struct clk *c, unsigned n)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+
+ if (!rcg->freq_tbl || rcg->freq_tbl->freq_hz == FREQ_END)
+ return -ENXIO;
+
+ return (rcg->freq_tbl + n)->freq_hz;
+}
+
+static struct clk *_rcg_clk_get_parent(struct rcg_clk *rcg, bool has_mnd,
+ bool match_rate)
+{
+ u32 n_regval = 0, m_regval = 0, d_regval = 0;
+ u32 cfg_regval, div, div_regval;
+ struct clk_freq_tbl *freq;
+ u32 cmd_rcgr_regval;
+
+ if (!rcg->freq_tbl) {
+ WARN(1, "No frequency table present for rcg %s\n",
+ rcg->c.dbg_name);
+ return NULL;
+ }
+
+ /* Is there a pending configuration? */
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
+ if (cmd_rcgr_regval & CMD_RCGR_CONFIG_DIRTY_MASK) {
+ WARN(1, "Pending transaction for rcg %s\n", rcg->c.dbg_name);
+ return NULL;
+ }
+
+ /* Get values of m, n, d, div and src_sel registers. */
+ if (has_mnd) {
+ m_regval = readl_relaxed(M_REG(rcg));
+ n_regval = readl_relaxed(N_REG(rcg));
+ d_regval = readl_relaxed(D_REG(rcg));
+
+ /*
+ * The n and d values stored in the frequency tables are sign
+ * extended to 32 bits. The n and d values in the registers are
+ * sign extended to 8 or 16 bits. Sign extend the values read
+ * from the registers so that they can be compared to the
+ * values in the frequency tables.
+ */
+ n_regval |= (n_regval >> 8) ? BM(31, 16) : BM(31, 8);
+ d_regval |= (d_regval >> 8) ? BM(31, 16) : BM(31, 8);
+ }
+
+ cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg));
+ cfg_regval &= CFG_RCGR_SRC_SEL_MASK | CFG_RCGR_DIV_MASK
+ | MND_MODE_MASK;
+
+ /* If mnd counter is present, check if it's in use. */
+ has_mnd = (has_mnd) &&
+ ((cfg_regval & MND_MODE_MASK) == MND_DUAL_EDGE_MODE_BVAL);
+
+ /*
+ * Clear out the mn counter mode bits since we now want to compare only
+ * the source mux selection and pre-divider values in the registers.
+ */
+ cfg_regval &= ~MND_MODE_MASK;
+
+ /* Figure out what rate the rcg is running at */
+ for (freq = rcg->freq_tbl; freq->freq_hz != FREQ_END; freq++) {
+ /* source select does not match */
+ if ((freq->div_src_val & CFG_RCGR_SRC_SEL_MASK)
+ != (cfg_regval & CFG_RCGR_SRC_SEL_MASK))
+ continue;
+ /*
+ * Stop if we found the required parent in the frequency table
+ * and only care if the source matches but dont care if the
+ * frequency matches
+ */
+ if (!match_rate)
+ break;
+ /* divider does not match */
+ div = freq->div_src_val & CFG_RCGR_DIV_MASK;
+ div_regval = cfg_regval & CFG_RCGR_DIV_MASK;
+ if (div != div_regval && (div > 1 || div_regval > 1))
+ continue;
+
+ if (has_mnd) {
+ if (freq->m_val != m_regval)
+ continue;
+ if (freq->n_val != n_regval)
+ continue;
+ if (freq->d_val != d_regval)
+ continue;
+ } else if (freq->n_val) {
+ continue;
+ }
+ break;
+ }
+
+ /* No known frequency found */
+ if (freq->freq_hz == FREQ_END) {
+ /*
+ * If we can't recognize the frequency and non_local_children is
+ * set, switch to safe frequency. It is assumed the current
+ * parent has been turned on by the bootchain if the RCG is on.
+ */
+ if (rcg->non_local_children) {
+ rcg->set_rate(rcg, &cxo_f);
+ WARN(1, "don't recognize rcg frequency for %s\n",
+ rcg->c.dbg_name);
+ }
+ return NULL;
+ }
+
+ rcg->current_freq = freq;
+ return freq->src_clk;
+}
+
+static enum handoff _rcg_clk_handoff(struct rcg_clk *rcg)
+{
+ u32 cmd_rcgr_regval;
+
+ if (rcg->current_freq && rcg->current_freq->freq_hz != FREQ_END)
+ rcg->c.rate = rcg->current_freq->freq_hz;
+
+ /* Is the root enabled? */
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
+ if ((cmd_rcgr_regval & CMD_RCGR_ROOT_STATUS_BIT))
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static struct clk *display_clk_get_parent(struct clk *c)
+{
+ return _rcg_clk_get_parent(to_rcg_clk(c), false, false);
+}
+
+static struct clk *rcg_mnd_clk_get_parent(struct clk *c)
+{
+ return _rcg_clk_get_parent(to_rcg_clk(c), true, true);
+}
+
+static struct clk *rcg_clk_get_parent(struct clk *c)
+{
+ return _rcg_clk_get_parent(to_rcg_clk(c), false, true);
+}
+
+static enum handoff rcg_mnd_clk_handoff(struct clk *c)
+{
+ return _rcg_clk_handoff(to_rcg_clk(c));
+}
+
+static enum handoff rcg_clk_handoff(struct clk *c)
+{
+ return _rcg_clk_handoff(to_rcg_clk(c));
+}
+
+static void __iomem *rcg_hid_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ static struct clk_register_data data[] = {
+ {"CMD_RCGR", 0x0},
+ {"CFG_RCGR", 0x4},
+ };
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return CMD_RCGR_REG(rcg);
+}
+
+static void __iomem *rcg_mnd_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ static struct clk_register_data data[] = {
+ {"CMD_RCGR", 0x0},
+ {"CFG_RCGR", 0x4},
+ {"M_VAL", 0x8},
+ {"N_VAL", 0xC},
+ {"D_VAL", 0x10},
+ };
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return CMD_RCGR_REG(rcg);
+}
+
+#define BRANCH_CHECK_MASK BM(31, 28)
+#define BRANCH_ON_VAL BVAL(31, 28, 0x0)
+#define BRANCH_OFF_VAL BVAL(31, 28, 0x8)
+#define BRANCH_NOC_FSM_ON_VAL BVAL(31, 28, 0x2)
+
+/*
+ * Branch clock functions
+ */
+static void branch_clk_halt_check(struct clk *c, u32 halt_check,
+ void __iomem *cbcr_reg, enum branch_state br_status)
+{
+ char *status_str = (br_status == BRANCH_ON) ? "off" : "on";
+
+ /*
+ * Use a memory barrier since some halt status registers are
+ * not within the same 1K segment as the branch/root enable
+ * registers. It's also needed in the udelay() case to ensure
+ * the delay starts after the branch disable.
+ */
+ mb();
+
+ if (halt_check == DELAY || halt_check == HALT_VOTED) {
+ udelay(HALT_CHECK_DELAY_US);
+ } else if (halt_check == HALT) {
+ int count;
+ u32 val;
+ for (count = HALT_CHECK_MAX_LOOPS; count > 0; count--) {
+ val = readl_relaxed(cbcr_reg);
+ val &= BRANCH_CHECK_MASK;
+ switch (br_status) {
+ case BRANCH_ON:
+ if (val == BRANCH_ON_VAL
+ || val == BRANCH_NOC_FSM_ON_VAL)
+ return;
+ break;
+
+ case BRANCH_OFF:
+ if (val == BRANCH_OFF_VAL)
+ return;
+ break;
+ };
+ udelay(1);
+ }
+ CLK_WARN(c, count == 0, "status stuck %s", status_str);
+ }
+}
+
+static int cbcr_set_flags(void * __iomem regaddr, unsigned flags)
+{
+ u32 cbcr_val;
+ unsigned long irq_flags;
+ int delay_us = 0, ret = 0;
+
+ spin_lock_irqsave(&local_clock_reg_lock, irq_flags);
+ cbcr_val = readl_relaxed(regaddr);
+ switch (flags) {
+ case CLKFLAG_RETAIN_PERIPH:
+ cbcr_val |= BIT(13);
+ delay_us = 1;
+ break;
+ case CLKFLAG_NORETAIN_PERIPH:
+ cbcr_val &= ~BIT(13);
+ break;
+ case CLKFLAG_RETAIN_MEM:
+ cbcr_val |= BIT(14);
+ delay_us = 1;
+ break;
+ case CLKFLAG_NORETAIN_MEM:
+ cbcr_val &= ~BIT(14);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ writel_relaxed(cbcr_val, regaddr);
+ /* Make sure power is enabled before returning. */
+ mb();
+ udelay(delay_us);
+
+ spin_unlock_irqrestore(&local_clock_reg_lock, irq_flags);
+
+ return ret;
+}
+
+static int branch_clk_set_flags(struct clk *c, unsigned flags)
+{
+ return cbcr_set_flags(CBCR_REG(to_branch_clk(c)), flags);
+}
+
+static int branch_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ u32 cbcr_val;
+ struct branch_clk *branch = to_branch_clk(c);
+
+ if (branch->toggle_memory) {
+ branch_clk_set_flags(c, CLKFLAG_RETAIN_MEM);
+ branch_clk_set_flags(c, CLKFLAG_RETAIN_PERIPH);
+ }
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ cbcr_val = readl_relaxed(CBCR_REG(branch));
+ cbcr_val |= CBCR_BRANCH_ENABLE_BIT;
+ writel_relaxed(cbcr_val, CBCR_REG(branch));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ /*
+ * For clocks controlled by other masters via voting registers,
+ * delay polling for the status bit to allow previous clk_disable
+ * by the GDS controller to go through.
+ */
+ if (branch->no_halt_check_on_disable)
+ udelay(5);
+
+ /* Wait for clock to enable before continuing. */
+ branch_clk_halt_check(c, branch->halt_check, CBCR_REG(branch),
+ BRANCH_ON);
+
+ return 0;
+}
+
+static void branch_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ struct branch_clk *branch = to_branch_clk(c);
+ u32 reg_val;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ reg_val = readl_relaxed(CBCR_REG(branch));
+ reg_val &= ~CBCR_BRANCH_ENABLE_BIT;
+ writel_relaxed(reg_val, CBCR_REG(branch));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ /* Wait for clock to disable before continuing. */
+ if (!branch->no_halt_check_on_disable)
+ branch_clk_halt_check(c, branch->halt_check, CBCR_REG(branch),
+ BRANCH_OFF);
+
+ if (branch->toggle_memory) {
+ branch_clk_set_flags(c, CLKFLAG_NORETAIN_MEM);
+ branch_clk_set_flags(c, CLKFLAG_NORETAIN_PERIPH);
+ }
+}
+
+static int branch_cdiv_set_rate(struct branch_clk *branch, unsigned long rate)
+{
+ unsigned long flags;
+ u32 regval;
+
+ if (rate > branch->max_div)
+ return -EINVAL;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ regval = readl_relaxed(CBCR_REG(branch));
+ regval &= ~BM(CBCR_CDIV_MSB, CBCR_CDIV_LSB);
+ regval |= BVAL(CBCR_CDIV_MSB, CBCR_CDIV_LSB, rate);
+ writel_relaxed(regval, CBCR_REG(branch));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ return 0;
+}
+
+static int branch_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ struct branch_clk *branch = to_branch_clk(c);
+
+ if (branch->max_div)
+ return branch_cdiv_set_rate(branch, rate);
+
+ if (!branch->has_sibling)
+ return clk_set_rate(c->parent, rate);
+
+ return -EPERM;
+}
+
+static long branch_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ struct branch_clk *branch = to_branch_clk(c);
+
+ if (branch->max_div)
+ return rate <= (branch->max_div) ? rate : -EPERM;
+
+ if (!branch->has_sibling)
+ return clk_round_rate(c->parent, rate);
+
+ return -EPERM;
+}
+
+static unsigned long branch_clk_get_rate(struct clk *c)
+{
+ struct branch_clk *branch = to_branch_clk(c);
+
+ if (branch->max_div)
+ return branch->c.rate;
+
+ return clk_get_rate(c->parent);
+}
+
+static long branch_clk_list_rate(struct clk *c, unsigned n)
+{
+ int level;
+ unsigned long fmax = 0, rate;
+ struct branch_clk *branch = to_branch_clk(c);
+ struct clk *parent = c->parent;
+
+ if (branch->has_sibling == 1)
+ return -ENXIO;
+
+ if (!parent || !parent->ops->list_rate)
+ return -ENXIO;
+
+ /* Find max frequency supported within voltage constraints. */
+ if (!parent->vdd_class) {
+ fmax = ULONG_MAX;
+ } else {
+ for (level = 0; level < parent->num_fmax; level++)
+ if (parent->fmax[level])
+ fmax = parent->fmax[level];
+ }
+
+ rate = parent->ops->list_rate(parent, n);
+ if (rate <= fmax)
+ return rate;
+ else
+ return -ENXIO;
+}
+
+static enum handoff branch_clk_handoff(struct clk *c)
+{
+ struct branch_clk *branch = to_branch_clk(c);
+ u32 cbcr_regval;
+
+ cbcr_regval = readl_relaxed(CBCR_REG(branch));
+
+ /* Set the cdiv to c->rate for fixed divider branch clock */
+ if (c->rate && (c->rate < branch->max_div)) {
+ cbcr_regval &= ~BM(CBCR_CDIV_MSB, CBCR_CDIV_LSB);
+ cbcr_regval |= BVAL(CBCR_CDIV_MSB, CBCR_CDIV_LSB, c->rate);
+ writel_relaxed(cbcr_regval, CBCR_REG(branch));
+ }
+
+ if ((cbcr_regval & CBCR_BRANCH_OFF_BIT))
+ return HANDOFF_DISABLED_CLK;
+
+ if (!(cbcr_regval & CBCR_BRANCH_ENABLE_BIT)) {
+ WARN(!branch->check_enable_bit,
+ "%s clock is enabled in HW even though ENABLE_BIT is not set\n",
+ c->dbg_name);
+ return HANDOFF_DISABLED_CLK;
+ }
+
+ if (branch->max_div) {
+ cbcr_regval &= BM(CBCR_CDIV_MSB, CBCR_CDIV_LSB);
+ cbcr_regval >>= CBCR_CDIV_LSB;
+ c->rate = cbcr_regval;
+ } else if (!branch->has_sibling) {
+ c->rate = clk_get_rate(c->parent);
+ }
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static int __branch_clk_reset(void __iomem *bcr_reg,
+ enum clk_reset_action action)
+{
+ int ret = 0;
+ unsigned long flags;
+ u32 reg_val;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ reg_val = readl_relaxed(bcr_reg);
+ switch (action) {
+ case CLK_RESET_ASSERT:
+ reg_val |= BCR_BLK_ARES_BIT;
+ break;
+ case CLK_RESET_DEASSERT:
+ reg_val &= ~BCR_BLK_ARES_BIT;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ writel_relaxed(reg_val, bcr_reg);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ /* Make sure write is issued before returning. */
+ mb();
+
+ return ret;
+}
+
+static int branch_clk_reset(struct clk *c, enum clk_reset_action action)
+{
+ struct branch_clk *branch = to_branch_clk(c);
+
+ if (!branch->bcr_reg)
+ return -EPERM;
+ return __branch_clk_reset(BCR_REG(branch), action);
+}
+
+static void __iomem *branch_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct branch_clk *branch = to_branch_clk(c);
+ static struct clk_register_data data[] = {
+ {"CBCR", 0x0},
+ };
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return CBCR_REG(branch);
+}
+
+/*
+ * Voteable clock functions
+ */
+static int local_vote_clk_reset(struct clk *c, enum clk_reset_action action)
+{
+ struct local_vote_clk *vclk = to_local_vote_clk(c);
+
+ if (!vclk->bcr_reg) {
+ WARN("clk_reset called on an unsupported clock (%s)\n",
+ c->dbg_name);
+ return -EPERM;
+ }
+ return __branch_clk_reset(BCR_REG(vclk), action);
+}
+
+static int local_vote_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ u32 ena;
+ struct local_vote_clk *vclk = to_local_vote_clk(c);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ ena = readl_relaxed(VOTE_REG(vclk));
+ ena |= vclk->en_mask;
+ writel_relaxed(ena, VOTE_REG(vclk));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ branch_clk_halt_check(c, vclk->halt_check, CBCR_REG(vclk), BRANCH_ON);
+
+ return 0;
+}
+
+static void local_vote_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ u32 ena;
+ struct local_vote_clk *vclk = to_local_vote_clk(c);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ ena = readl_relaxed(VOTE_REG(vclk));
+ ena &= ~vclk->en_mask;
+ writel_relaxed(ena, VOTE_REG(vclk));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static enum handoff local_vote_clk_handoff(struct clk *c)
+{
+ struct local_vote_clk *vclk = to_local_vote_clk(c);
+ u32 vote_regval;
+
+ /* Is the branch voted on by apps? */
+ vote_regval = readl_relaxed(VOTE_REG(vclk));
+ if (!(vote_regval & vclk->en_mask))
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+/* Sample clock for 'ticks' reference clock ticks. */
+static u32 run_measurement(unsigned ticks, void __iomem *ctl_reg,
+ void __iomem *status_reg)
+{
+ /* Stop counters and set the XO4 counter start value. */
+ writel_relaxed(ticks, ctl_reg);
+
+ /* Wait for timer to become ready. */
+ while ((readl_relaxed(status_reg) & BIT(25)) != 0)
+ cpu_relax();
+
+ /* Run measurement and wait for completion. */
+ writel_relaxed(BIT(20)|ticks, ctl_reg);
+ while ((readl_relaxed(status_reg) & BIT(25)) == 0)
+ cpu_relax();
+
+ /* Return measured ticks. */
+ return readl_relaxed(status_reg) & BM(24, 0);
+}
+
+/*
+ * Perform a hardware rate measurement for a given clock.
+ * FOR DEBUG USE ONLY: Measurements take ~15 ms!
+ */
+unsigned long measure_get_rate(struct clk *c)
+{
+ unsigned long flags;
+ u32 gcc_xo4_reg, regval;
+ u64 raw_count_short, raw_count_full;
+ unsigned ret;
+ u32 sample_ticks = 0x10000;
+ u32 multiplier = 0x1;
+ struct measure_clk_data *data = to_mux_clk(c)->priv;
+
+ regval = readl_relaxed(MUX_REG(to_mux_clk(c)));
+ /* clear post divider bits */
+ regval &= ~BM(15, 12);
+ writel_relaxed(regval, MUX_REG(to_mux_clk(c)));
+
+ ret = clk_prepare_enable(data->cxo);
+ if (ret) {
+ pr_warn("CXO clock failed to enable. Can't measure\n");
+ return 0;
+ }
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+
+ /* Enable CXO/4 and RINGOSC branch. */
+ gcc_xo4_reg = readl_relaxed(*data->base + data->xo_div4_cbcr);
+ gcc_xo4_reg |= CBCR_BRANCH_ENABLE_BIT;
+ writel_relaxed(gcc_xo4_reg, *data->base + data->xo_div4_cbcr);
+
+ /*
+ * The ring oscillator counter will not reset if the measured clock
+ * is not running. To detect this, run a short measurement before
+ * the full measurement. If the raw results of the two are the same
+ * then the clock must be off.
+ */
+
+ /* Run a short measurement. (~1 ms) */
+ raw_count_short = run_measurement(0x1000, *data->base + data->ctl_reg,
+ *data->base + data->status_reg);
+ /* Run a full measurement. (~14 ms) */
+ raw_count_full = run_measurement(sample_ticks,
+ *data->base + data->ctl_reg,
+ *data->base + data->status_reg);
+
+ gcc_xo4_reg &= ~CBCR_BRANCH_ENABLE_BIT;
+ writel_relaxed(gcc_xo4_reg, *data->base + data->xo_div4_cbcr);
+
+ /* Return 0 if the clock is off. */
+ if (raw_count_full == raw_count_short) {
+ ret = 0;
+ } else {
+ /* Compute rate in Hz. */
+ raw_count_full = ((raw_count_full * 10) + 15) * 4800000;
+ do_div(raw_count_full, ((sample_ticks * 10) + 35));
+ ret = (raw_count_full * multiplier);
+ }
+ writel_relaxed(data->plltest_val, *data->base + data->plltest_reg);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ clk_disable_unprepare(data->cxo);
+
+ return ret;
+}
+
+struct frac_entry {
+ int num;
+ int den;
+};
+
+static void __iomem *local_vote_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct local_vote_clk *vclk = to_local_vote_clk(c);
+ static struct clk_register_data data1[] = {
+ {"CBCR", 0x0},
+ };
+ static struct clk_register_data data2[] = {
+ {"APPS_VOTE", 0x0},
+ {"APPS_SLEEP_VOTE", 0x4},
+ };
+ switch (n) {
+ case 0:
+ *regs = data1;
+ *size = ARRAY_SIZE(data1);
+ return CBCR_REG(vclk);
+ case 1:
+ *regs = data2;
+ *size = ARRAY_SIZE(data2);
+ return VOTE_REG(vclk);
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static struct frac_entry frac_table_675m[] = { /* link rate of 270M */
+ {52, 295}, /* 119 M */
+ {11, 57}, /* 130.25 M */
+ {63, 307}, /* 138.50 M */
+ {11, 50}, /* 148.50 M */
+ {47, 206}, /* 154 M */
+ {31, 100}, /* 205.25 M */
+ {107, 269}, /* 268.50 M */
+ {0, 0},
+};
+
+static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */
+ {31, 211}, /* 119 M */
+ {32, 199}, /* 130.25 M */
+ {63, 307}, /* 138.50 M */
+ {11, 60}, /* 148.50 M */
+ {50, 263}, /* 154 M */
+ {31, 120}, /* 205.25 M */
+ {119, 359}, /* 268.50 M */
+ {0, 0},
+};
+
+static bool is_same_rcg_config(struct rcg_clk *rcg, struct clk_freq_tbl *freq,
+ bool has_mnd)
+{
+ u32 cfg;
+
+ /* RCG update pending */
+ if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_CONFIG_DIRTY_MASK)
+ return false;
+ if (has_mnd)
+ if (readl_relaxed(M_REG(rcg)) != freq->m_val ||
+ readl_relaxed(N_REG(rcg)) != freq->n_val ||
+ readl_relaxed(D_REG(rcg)) != freq->d_val)
+ return false;
+ /*
+ * Both 0 and 1 represent same divider value in HW.
+ * Always use 0 to simplify comparison.
+ */
+ if ((freq->div_src_val & CFG_RCGR_DIV_MASK) == 1)
+ freq->div_src_val &= ~CFG_RCGR_DIV_MASK;
+ cfg = readl_relaxed(CFG_RCGR_REG(rcg));
+ if ((cfg & CFG_RCGR_DIV_MASK) == 1)
+ cfg &= ~CFG_RCGR_DIV_MASK;
+ if (cfg != freq->div_src_val)
+ return false;
+
+ return true;
+}
+
+static int set_rate_edp_pixel(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk_freq_tbl *pixel_freq = rcg->current_freq;
+ struct frac_entry *frac;
+ int delta = 100000;
+ s64 request;
+ s64 src_rate;
+ unsigned long flags;
+
+ src_rate = clk_get_rate(clk->parent);
+
+ if (src_rate == 810000000)
+ frac = frac_table_810m;
+ else
+ frac = frac_table_675m;
+
+ while (frac->num) {
+ request = rate;
+ request *= frac->den;
+ request = div_s64(request, frac->num);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta))) {
+ frac++;
+ continue;
+ }
+
+ pixel_freq->div_src_val &= ~BM(4, 0);
+ if (frac->den == frac->num) {
+ pixel_freq->m_val = 0;
+ pixel_freq->n_val = 0;
+ } else {
+ pixel_freq->m_val = frac->num;
+ pixel_freq->n_val = ~(frac->den - frac->num);
+ pixel_freq->d_val = ~frac->den;
+ }
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ if (!is_same_rcg_config(rcg, pixel_freq, true))
+ __set_rate_mnd(rcg, pixel_freq);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+enum handoff byte_rcg_handoff(struct clk *clk)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ u32 div_val;
+ unsigned long pre_div_rate, parent_rate = clk_get_rate(clk->parent);
+
+ /* If the pre-divider is used, find the rate after the division */
+ div_val = readl_relaxed(CFG_RCGR_REG(rcg)) & CFG_RCGR_DIV_MASK;
+ if (div_val > 1)
+ pre_div_rate = parent_rate / ((div_val + 1) >> 1);
+ else
+ pre_div_rate = parent_rate;
+
+ clk->rate = pre_div_rate;
+
+ if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_ROOT_STATUS_BIT)
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static int set_rate_byte(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk *pll = clk->parent;
+ unsigned long source_rate, div, flags;
+ struct clk_freq_tbl *byte_freq = rcg->current_freq;
+ int rc;
+
+ if (rate == 0)
+ return -EINVAL;
+
+ rc = clk_set_rate(pll, rate);
+ if (rc)
+ return rc;
+
+ source_rate = clk_round_rate(pll, rate);
+ if ((2 * source_rate) % rate)
+ return -EINVAL;
+
+ div = ((2 * source_rate)/rate) - 1;
+ if (div > CFG_RCGR_DIV_MASK)
+ return -EINVAL;
+
+ /*
+ * Both 0 and 1 represent same divider value in HW.
+ * Always use 0 to simplify comparison.
+ */
+ div = (div == 1) ? 0 : div;
+
+ byte_freq->div_src_val &= ~CFG_RCGR_DIV_MASK;
+ byte_freq->div_src_val |= BVAL(4, 0, div);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ if (!is_same_rcg_config(rcg, byte_freq, false))
+ __set_rate_hid(rcg, byte_freq);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ return 0;
+}
+
+enum handoff pixel_rcg_handoff(struct clk *clk)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ u32 div_val = 0, mval = 0, nval = 0, cfg_regval;
+ unsigned long pre_div_rate, parent_rate = clk_get_rate(clk->parent);
+
+ cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg));
+
+ /* If the pre-divider is used, find the rate after the division */
+ div_val = cfg_regval & CFG_RCGR_DIV_MASK;
+ if (div_val > 1)
+ pre_div_rate = parent_rate / ((div_val + 1) >> 1);
+ else
+ pre_div_rate = parent_rate;
+
+ clk->rate = pre_div_rate;
+
+ /*
+ * Pixel clocks have one frequency entry in their frequency table.
+ * Update that entry.
+ */
+ if (rcg->current_freq) {
+ rcg->current_freq->div_src_val &= ~CFG_RCGR_DIV_MASK;
+ rcg->current_freq->div_src_val |= div_val;
+ }
+
+ /* If MND is used, find the rate after the MND division */
+ if ((cfg_regval & MND_MODE_MASK) == MND_DUAL_EDGE_MODE_BVAL) {
+ mval = readl_relaxed(M_REG(rcg));
+ nval = readl_relaxed(N_REG(rcg));
+ if (!nval)
+ return HANDOFF_DISABLED_CLK;
+ nval = (~nval) + mval;
+ if (rcg->current_freq) {
+ rcg->current_freq->n_val = ~(nval - mval);
+ rcg->current_freq->m_val = mval;
+ rcg->current_freq->d_val = ~nval;
+ }
+ clk->rate = (pre_div_rate * mval) / nval;
+ }
+
+ if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_ROOT_STATUS_BIT)
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static long round_rate_pixel(struct clk *clk, unsigned long rate)
+{
+ int frac_num[] = {3, 2, 4, 1};
+ int frac_den[] = {8, 9, 9, 1};
+ int delta = 100000;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(frac_num); i++) {
+ unsigned long request = (rate * frac_den[i]) / frac_num[i];
+ unsigned long src_rate;
+
+ src_rate = clk_round_rate(clk->parent, request);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ return (src_rate * frac_num[i]) / frac_den[i];
+ }
+
+ return -EINVAL;
+}
+
+
+static int set_rate_pixel(struct clk *clk, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk_freq_tbl *pixel_freq = rcg->current_freq;
+ int frac_num[] = {3, 2, 4, 1};
+ int frac_den[] = {8, 9, 9, 1};
+ int delta = 100000;
+ int i, rc;
+
+ for (i = 0; i < ARRAY_SIZE(frac_num); i++) {
+ unsigned long request = (rate * frac_den[i]) / frac_num[i];
+ unsigned long src_rate;
+
+ src_rate = clk_round_rate(clk->parent, request);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ rc = clk_set_rate(clk->parent, src_rate);
+ if (rc)
+ return rc;
+
+ pixel_freq->div_src_val &= ~BM(4, 0);
+ if (frac_den[i] == frac_num[i]) {
+ pixel_freq->m_val = 0;
+ pixel_freq->n_val = 0;
+ } else {
+ pixel_freq->m_val = frac_num[i];
+ pixel_freq->n_val = ~(frac_den[i] - frac_num[i]);
+ pixel_freq->d_val = ~frac_den[i];
+ }
+ set_rate_mnd(rcg, pixel_freq);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int rcg_clk_set_parent(struct clk *clk, struct clk *parent_clk)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ struct clk *old_parent = clk->parent;
+ struct clk_freq_tbl *nf;
+ unsigned long flags;
+ int rc = 0;
+ unsigned int parent_rate, rate;
+ u32 m_val, n_val, d_val, div_val;
+ u32 cfg_regval;
+
+ /* Find the source clock freq tbl for the requested parent */
+ if (!rcg->freq_tbl)
+ return -ENXIO;
+
+ for (nf = rcg->freq_tbl; parent_clk != nf->src_clk; nf++) {
+ if (nf->freq_hz == FREQ_END)
+ return -ENXIO;
+ }
+
+ /* This implementation recommends that the RCG be unprepared
+ * when switching RCG source since the divider configuration
+ * remains unchanged.
+ */
+ WARN(clk->prepare_count,
+ "Trying to switch RCG source while it is prepared!\n");
+
+ parent_rate = clk_get_rate(parent_clk);
+
+ div_val = (rcg->current_freq->div_src_val & CFG_RCGR_DIV_MASK);
+ if (div_val)
+ parent_rate /= ((div_val + 1) >> 1);
+
+ /* Update divisor. Source select bits should already be as expected */
+ nf->div_src_val &= ~CFG_RCGR_DIV_MASK;
+ nf->div_src_val |= div_val;
+
+ cfg_regval = readl_relaxed(CFG_RCGR_REG(rcg));
+
+ if ((cfg_regval & MND_MODE_MASK) == MND_DUAL_EDGE_MODE_BVAL) {
+ nf->m_val = m_val = readl_relaxed(M_REG(rcg));
+ n_val = readl_relaxed(N_REG(rcg));
+ d_val = readl_relaxed(D_REG(rcg));
+
+ /* Sign extend the n and d values as those in registers are not
+ * sign extended.
+ */
+ n_val |= (n_val >> 8) ? BM(31, 16) : BM(31, 8);
+ d_val |= (d_val >> 8) ? BM(31, 16) : BM(31, 8);
+
+ nf->n_val = n_val;
+ nf->d_val = d_val;
+
+ n_val = ~(n_val) + m_val;
+ rate = parent_rate * m_val;
+ if (n_val)
+ rate /= n_val;
+ else
+ WARN(1, "n_val was 0!!");
+ } else
+ rate = parent_rate;
+
+ /* Warn if switching to the new parent with the current m, n ,d values
+ * violates the voltage constraints for the RCG.
+ */
+ WARN(!is_rate_valid(clk, rate) && clk->prepare_count,
+ "Switch to new RCG parent violates voltage requirement!\n");
+
+ rc = __clk_pre_reparent(clk, nf->src_clk, &flags);
+ if (rc)
+ return rc;
+
+ /* Switch RCG source */
+ rcg->set_rate(rcg, nf);
+
+ rcg->current_freq = nf;
+ clk->parent = parent_clk;
+ clk->rate = rate;
+
+ __clk_post_reparent(clk, old_parent, &flags);
+
+ return 0;
+}
+
+/*
+ * Unlike other clocks, the HDMI rate is adjusted through PLL
+ * re-programming. It is also routed through an HID divider.
+ */
+static int rcg_clk_set_rate_hdmi(struct clk *c, unsigned long rate)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ struct clk_freq_tbl *nf = rcg->freq_tbl;
+ int rc;
+
+ rc = clk_set_rate(nf->src_clk, rate);
+ if (rc < 0)
+ goto out;
+ set_rate_hid(rcg, nf);
+
+ rcg->current_freq = nf;
+out:
+ return rc;
+}
+
+static struct clk *rcg_hdmi_clk_get_parent(struct clk *c)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ struct clk_freq_tbl *freq = rcg->freq_tbl;
+ u32 cmd_rcgr_regval;
+
+ /* Is there a pending configuration? */
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
+ if (cmd_rcgr_regval & CMD_RCGR_CONFIG_DIRTY_MASK)
+ return NULL;
+
+ rcg->current_freq->freq_hz = clk_get_rate(c->parent);
+
+ return freq->src_clk;
+}
+
+static int rcg_clk_set_rate_edp(struct clk *c, unsigned long rate)
+{
+ struct clk_freq_tbl *nf;
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ int rc;
+
+ for (nf = rcg->freq_tbl; nf->freq_hz != rate; nf++)
+ if (nf->freq_hz == FREQ_END) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = clk_set_rate(nf->src_clk, rate);
+ if (rc < 0)
+ goto out;
+ set_rate_hid(rcg, nf);
+
+ rcg->current_freq = nf;
+ c->parent = nf->src_clk;
+out:
+ return rc;
+}
+
+static struct clk *edp_clk_get_parent(struct clk *c)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ struct clk *clk;
+ struct clk_freq_tbl *freq;
+ unsigned long rate;
+ u32 cmd_rcgr_regval;
+
+ /* Is there a pending configuration? */
+ cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg));
+ if (cmd_rcgr_regval & CMD_RCGR_CONFIG_DIRTY_MASK)
+ return NULL;
+
+ /* Figure out what rate the rcg is running at */
+ for (freq = rcg->freq_tbl; freq->freq_hz != FREQ_END; freq++) {
+ clk = freq->src_clk;
+ if (clk && clk->ops->get_rate) {
+ rate = clk->ops->get_rate(clk);
+ if (rate == freq->freq_hz)
+ break;
+ }
+ }
+
+ /* No known frequency found */
+ if (freq->freq_hz == FREQ_END)
+ return NULL;
+
+ rcg->current_freq = freq;
+ return freq->src_clk;
+}
+
+static int gate_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ u32 regval;
+ struct gate_clk *g = to_gate_clk(c);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ regval = readl_relaxed(GATE_EN_REG(g));
+ regval |= g->en_mask;
+ writel_relaxed(regval, GATE_EN_REG(g));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+ if (g->delay_us)
+ udelay(g->delay_us);
+
+ return 0;
+}
+
+static void gate_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ u32 regval;
+ struct gate_clk *g = to_gate_clk(c);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ regval = readl_relaxed(GATE_EN_REG(g));
+ regval &= ~(g->en_mask);
+ writel_relaxed(regval, GATE_EN_REG(g));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+ if (g->delay_us)
+ udelay(g->delay_us);
+}
+
+static void __iomem *gate_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct gate_clk *g = to_gate_clk(c);
+ static struct clk_register_data data[] = {
+ {"EN_REG", 0x0},
+ };
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return GATE_EN_REG(g);
+}
+
+static enum handoff gate_clk_handoff(struct clk *c)
+{
+ struct gate_clk *g = to_gate_clk(c);
+ u32 regval;
+
+ regval = readl_relaxed(GATE_EN_REG(g));
+ if (regval & g->en_mask)
+ return HANDOFF_ENABLED_CLK;
+
+ return HANDOFF_DISABLED_CLK;
+}
+
+static int gate_clk_set_flags(struct clk *c, unsigned flags)
+{
+ return cbcr_set_flags(GATE_EN_REG(to_gate_clk(c)), flags);
+}
+
+
+static int reset_clk_rst(struct clk *c, enum clk_reset_action action)
+{
+ struct reset_clk *rst = to_reset_clk(c);
+
+ if (!rst->reset_reg)
+ return -EPERM;
+
+ return __branch_clk_reset(RST_REG(rst), action);
+}
+
+static void __iomem *reset_clk_list_registers(struct clk *clk, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct reset_clk *rst = to_reset_clk(clk);
+ static struct clk_register_data data[] = {
+ {"BCR", 0x0},
+ };
+
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return RST_REG(rst);
+}
+
+static DEFINE_SPINLOCK(mux_reg_lock);
+
+static int mux_reg_enable(struct mux_clk *clk)
+{
+ u32 regval;
+ unsigned long flags;
+
+ if (!clk->en_mask)
+ return 0;
+
+ spin_lock_irqsave(&mux_reg_lock, flags);
+ regval = readl_relaxed(*clk->base + clk->en_offset);
+ regval |= clk->en_mask;
+ writel_relaxed(regval, *clk->base + clk->en_offset);
+ /* Ensure enable request goes through before returning */
+ mb();
+ spin_unlock_irqrestore(&mux_reg_lock, flags);
+
+ return 0;
+}
+
+static void mux_reg_disable(struct mux_clk *clk)
+{
+ u32 regval;
+ unsigned long flags;
+
+ if (!clk->en_mask)
+ return;
+
+ spin_lock_irqsave(&mux_reg_lock, flags);
+ regval = readl_relaxed(*clk->base + clk->en_offset);
+ regval &= ~clk->en_mask;
+ writel_relaxed(regval, *clk->base + clk->en_offset);
+ spin_unlock_irqrestore(&mux_reg_lock, flags);
+}
+
+static int mux_reg_set_mux_sel(struct mux_clk *clk, int sel)
+{
+ u32 regval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mux_reg_lock, flags);
+ regval = readl_relaxed(MUX_REG(clk));
+ regval &= ~(clk->mask << clk->shift);
+ regval |= (sel & clk->mask) << clk->shift;
+ writel_relaxed(regval, MUX_REG(clk));
+ /* Ensure switch request goes through before returning */
+ mb();
+ spin_unlock_irqrestore(&mux_reg_lock, flags);
+
+ return 0;
+}
+
+static int mux_reg_get_mux_sel(struct mux_clk *clk)
+{
+ u32 regval = readl_relaxed(MUX_REG(clk));
+ return (regval >> clk->shift) & clk->mask;
+}
+
+static bool mux_reg_is_enabled(struct mux_clk *clk)
+{
+ u32 regval = readl_relaxed(MUX_REG(clk));
+ return !!(regval & clk->en_mask);
+}
+
+static void __iomem *mux_clk_list_registers(struct mux_clk *clk, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ static struct clk_register_data data[] = {
+ {"DEBUG_CLK_CTL", 0x0},
+ };
+
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return *clk->base + clk->offset;
+}
+
+/* PLL post-divider setting for each divider value */
+static struct div_map postdiv_map[] = {
+ { 0x0, 1 },
+ { 0x1, 2 },
+ { 0x3, 3 },
+ { 0x3, 4 },
+ { 0x5, 5 },
+ { 0x7, 7 },
+ { 0x7, 8 },
+ { 0xF, 16 },
+};
+
+static int postdiv_reg_set_div(struct div_clk *clk, int div)
+{
+ struct clk *parent = NULL;
+ u32 regval;
+ unsigned long flags;
+ unsigned int mask = -1;
+ int i, ret = 0;
+
+ /* Divider is not configurable */
+ if (!clk->mask)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(postdiv_map); i++) {
+ if (postdiv_map[i].div == div) {
+ mask = postdiv_map[i].mask;
+ break;
+ }
+ }
+
+ if (mask < 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&clk->c.lock, flags);
+ parent = clk->c.parent;
+ if (parent->count && parent->ops->disable)
+ parent->ops->disable(parent);
+
+ regval = readl_relaxed(DIV_REG(clk));
+ regval &= ~(clk->mask << clk->shift);
+ regval |= (mask & clk->mask) << clk->shift;
+ writel_relaxed(regval, DIV_REG(clk));
+ /* Ensure switch request goes through before returning */
+ mb();
+
+ if (parent->count && parent->ops->enable) {
+ ret = parent->ops->enable(parent);
+ if (ret)
+ pr_err("Failed to force enable div parent!\n");
+ }
+
+ spin_unlock_irqrestore(&clk->c.lock, flags);
+ return ret;
+}
+
+static int postdiv_reg_get_div(struct div_clk *clk)
+{
+ u32 regval;
+ int i, div = 0;
+
+ /* Divider is not configurable */
+ if (!clk->mask)
+ return clk->data.div;
+
+ regval = readl_relaxed(DIV_REG(clk));
+ regval = (regval >> clk->shift) & clk->mask;
+ for (i = 0; i < ARRAY_SIZE(postdiv_map); i++) {
+ if (postdiv_map[i].mask == regval) {
+ div = postdiv_map[i].div;
+ break;
+ }
+ }
+ if (!div)
+ return -EINVAL;
+
+ return div;
+}
+
+static int div_reg_set_div(struct div_clk *clk, int div)
+{
+ u32 regval;
+ unsigned long flags;
+
+ /* Divider is not configurable */
+ if (!clk->mask)
+ return 0;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ regval = readl_relaxed(*clk->base + clk->offset);
+ regval &= ~(clk->mask << clk->shift);
+ regval |= (div & clk->mask) << clk->shift;
+ /* Ensure switch request goes through before returning */
+ mb();
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ return 0;
+}
+
+static int div_reg_get_div(struct div_clk *clk)
+{
+ u32 regval;
+ /* Divider is not configurable */
+ if (!clk->mask)
+ return clk->data.div;
+
+ regval = readl_relaxed(*clk->base + clk->offset);
+ return (regval >> clk->shift) & clk->mask;
+}
+
+/* =================Half-integer RCG without MN counter================= */
+#define RCGR_CMD_REG(x) ((x)->base + (x)->div_offset)
+#define RCGR_DIV_REG(x) ((x)->base + (x)->div_offset + 4)
+#define RCGR_SRC_REG(x) ((x)->base + (x)->div_offset + 4)
+
+static int rcg_mux_div_update_config(struct mux_div_clk *md)
+{
+ u32 regval, count;
+
+ regval = readl_relaxed(RCGR_CMD_REG(md));
+ regval |= CMD_RCGR_CONFIG_UPDATE_BIT;
+ writel_relaxed(regval, RCGR_CMD_REG(md));
+
+ /* Wait for update to take effect */
+ for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) {
+ if (!(readl_relaxed(RCGR_CMD_REG(md)) &
+ CMD_RCGR_CONFIG_UPDATE_BIT))
+ return 0;
+ udelay(1);
+ }
+
+ CLK_WARN(&md->c, true, "didn't update its configuration.");
+
+ return -EBUSY;
+}
+
+static void rcg_get_src_div(struct mux_div_clk *md, u32 *src_sel, u32 *div)
+{
+ u32 regval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ /* Is there a pending configuration? */
+ regval = readl_relaxed(RCGR_CMD_REG(md));
+ if (regval & CMD_RCGR_CONFIG_DIRTY_MASK) {
+ CLK_WARN(&md->c, true, "it's a pending configuration.");
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+ return;
+ }
+
+ regval = readl_relaxed(RCGR_DIV_REG(md));
+ regval &= (md->div_mask << md->div_shift);
+ *div = regval >> md->div_shift;
+
+ /* bypass */
+ if (*div == 0)
+ *div = 1;
+ /* the div is doubled here*/
+ *div += 1;
+
+ regval = readl_relaxed(RCGR_SRC_REG(md));
+ regval &= (md->src_mask << md->src_shift);
+ *src_sel = regval >> md->src_shift;
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static void mux_div_set_force_enable(struct mux_div_clk *md)
+{
+ u32 regval;
+ unsigned long flags;
+ int count;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ regval = readl_relaxed(RCGR_CMD_REG(md));
+ regval |= CMD_RCGR_ROOT_ENABLE_BIT;
+ writel_relaxed(regval, RCGR_CMD_REG(md));
+
+ /* Wait for RCG to turn ON */
+ for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) {
+ if (!(readl_relaxed(RCGR_CMD_REG(md)) &
+ CMD_RCGR_CONFIG_UPDATE_BIT))
+ goto exit;
+ udelay(1);
+ }
+ CLK_WARN(&md->c, count == 0, "rcg didn't turn on.");
+exit:
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static void mux_div_clear_force_enable(struct mux_div_clk *md)
+{
+ u32 regval;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ regval = readl_relaxed(RCGR_CMD_REG(md));
+ regval &= ~CMD_RCGR_ROOT_ENABLE_BIT;
+ writel_relaxed(regval, RCGR_CMD_REG(md));
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static int rcg_set_src_div(struct mux_div_clk *md, u32 src_sel, u32 div)
+{
+ u32 regval;
+ unsigned long flags;
+ int ret;
+
+ /* for half-integer divider, div here is doubled */
+ if (div)
+ div -= 1;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ regval = readl_relaxed(RCGR_DIV_REG(md));
+ regval &= ~(md->div_mask << md->div_shift);
+ regval |= div << md->div_shift;
+ writel_relaxed(regval, RCGR_DIV_REG(md));
+
+ regval = readl_relaxed(RCGR_SRC_REG(md));
+ regval &= ~(md->src_mask << md->src_shift);
+ regval |= src_sel << md->src_shift;
+ writel_relaxed(regval, RCGR_SRC_REG(md));
+
+ ret = rcg_mux_div_update_config(md);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+ return ret;
+}
+
+static int rcg_enable(struct mux_div_clk *md)
+{
+ if (md->force_enable_md)
+ mux_div_set_force_enable(md);
+
+ return rcg_set_src_div(md, md->src_sel, md->data.div);
+}
+
+static void rcg_disable(struct mux_div_clk *md)
+{
+ u32 src_sel;
+
+ if (md->force_enable_md)
+ mux_div_clear_force_enable(md);
+
+ if (!md->safe_freq)
+ return;
+
+ src_sel = parent_to_src_sel(md->parents, md->num_parents,
+ md->safe_parent);
+
+ rcg_set_src_div(md, src_sel, md->safe_div);
+}
+
+static bool rcg_is_enabled(struct mux_div_clk *md)
+{
+ u32 regval;
+
+ regval = readl_relaxed(RCGR_CMD_REG(md));
+ if (regval & CMD_RCGR_ROOT_STATUS_BIT)
+ return false;
+ else
+ return true;
+}
+
+static void __iomem *rcg_list_registers(struct mux_div_clk *md, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ static struct clk_register_data data[] = {
+ {"CMD_RCGR", 0x0},
+ {"CFG_RCGR", 0x4},
+ };
+
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return RCGR_CMD_REG(md);
+}
+
+struct clk_ops clk_ops_empty;
+
+struct clk_ops clk_ops_rst = {
+ .reset = reset_clk_rst,
+ .list_registers = reset_clk_list_registers,
+};
+
+struct clk_ops clk_ops_rcg = {
+ .enable = rcg_clk_enable,
+ .disable = rcg_clk_disable,
+ .set_rate = rcg_clk_set_rate,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = rcg_clk_handoff,
+ .get_parent = rcg_clk_get_parent,
+ .set_parent = rcg_clk_set_parent,
+ .list_registers = rcg_hid_clk_list_registers,
+};
+
+struct clk_ops clk_ops_rcg_mnd = {
+ .enable = rcg_clk_enable,
+ .disable = rcg_clk_disable,
+ .set_rate = rcg_clk_set_rate,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = rcg_mnd_clk_handoff,
+ .get_parent = rcg_mnd_clk_get_parent,
+ .set_parent = rcg_clk_set_parent,
+ .list_registers = rcg_mnd_clk_list_registers,
+};
+
+struct clk_ops clk_ops_pixel = {
+ .enable = rcg_clk_enable,
+ .disable = rcg_clk_disable,
+ .set_rate = set_rate_pixel,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = round_rate_pixel,
+ .handoff = pixel_rcg_handoff,
+ .list_registers = rcg_mnd_clk_list_registers,
+};
+
+struct clk_ops clk_ops_pixel_multiparent = {
+ .enable = rcg_clk_enable,
+ .disable = rcg_clk_disable,
+ .set_rate = set_rate_pixel,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = round_rate_pixel,
+ .handoff = pixel_rcg_handoff,
+ .list_registers = rcg_mnd_clk_list_registers,
+ .get_parent = display_clk_get_parent,
+ .set_parent = rcg_clk_set_parent,
+};
+
+struct clk_ops clk_ops_edppixel = {
+ .enable = rcg_clk_enable,
+ .disable = rcg_clk_disable,
+ .set_rate = set_rate_edp_pixel,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = pixel_rcg_handoff,
+ .list_registers = rcg_mnd_clk_list_registers,
+};
+
+struct clk_ops clk_ops_byte = {
+ .enable = rcg_clk_enable,
+ .disable = rcg_clk_disable,
+ .set_rate = set_rate_byte,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = byte_rcg_handoff,
+ .list_registers = rcg_hid_clk_list_registers,
+};
+
+struct clk_ops clk_ops_byte_multiparent = {
+ .enable = rcg_clk_enable,
+ .disable = rcg_clk_disable,
+ .set_rate = set_rate_byte,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = byte_rcg_handoff,
+ .list_registers = rcg_hid_clk_list_registers,
+ .get_parent = display_clk_get_parent,
+ .set_parent = rcg_clk_set_parent,
+};
+
+struct clk_ops clk_ops_rcg_hdmi = {
+ .enable = rcg_clk_enable,
+ .disable = rcg_clk_disable,
+ .set_rate = rcg_clk_set_rate_hdmi,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = rcg_clk_handoff,
+ .get_parent = rcg_hdmi_clk_get_parent,
+ .list_registers = rcg_hid_clk_list_registers,
+};
+
+struct clk_ops clk_ops_rcg_edp = {
+ .enable = rcg_clk_enable,
+ .disable = rcg_clk_disable,
+ .set_rate = rcg_clk_set_rate_edp,
+ .list_rate = rcg_clk_list_rate,
+ .round_rate = rcg_clk_round_rate,
+ .handoff = rcg_clk_handoff,
+ .get_parent = edp_clk_get_parent,
+ .list_registers = rcg_hid_clk_list_registers,
+};
+
+struct clk_ops clk_ops_branch = {
+ .enable = branch_clk_enable,
+ .disable = branch_clk_disable,
+ .set_rate = branch_clk_set_rate,
+ .get_rate = branch_clk_get_rate,
+ .list_rate = branch_clk_list_rate,
+ .round_rate = branch_clk_round_rate,
+ .reset = branch_clk_reset,
+ .set_flags = branch_clk_set_flags,
+ .handoff = branch_clk_handoff,
+ .list_registers = branch_clk_list_registers,
+};
+
+struct clk_ops clk_ops_vote = {
+ .enable = local_vote_clk_enable,
+ .disable = local_vote_clk_disable,
+ .reset = local_vote_clk_reset,
+ .handoff = local_vote_clk_handoff,
+ .list_registers = local_vote_clk_list_registers,
+};
+
+struct clk_ops clk_ops_gate = {
+ .enable = gate_clk_enable,
+ .disable = gate_clk_disable,
+ .set_rate = parent_set_rate,
+ .get_rate = parent_get_rate,
+ .round_rate = parent_round_rate,
+ .set_flags = gate_clk_set_flags,
+ .handoff = gate_clk_handoff,
+ .list_registers = gate_clk_list_registers,
+};
+
+struct clk_mux_ops mux_reg_ops = {
+ .enable = mux_reg_enable,
+ .disable = mux_reg_disable,
+ .set_mux_sel = mux_reg_set_mux_sel,
+ .get_mux_sel = mux_reg_get_mux_sel,
+ .is_enabled = mux_reg_is_enabled,
+ .list_registers = mux_clk_list_registers,
+};
+
+struct clk_div_ops div_reg_ops = {
+ .set_div = div_reg_set_div,
+ .get_div = div_reg_get_div,
+};
+
+struct clk_div_ops postdiv_reg_ops = {
+ .set_div = postdiv_reg_set_div,
+ .get_div = postdiv_reg_get_div,
+};
+
+struct mux_div_ops rcg_mux_div_ops = {
+ .enable = rcg_enable,
+ .disable = rcg_disable,
+ .set_src_div = rcg_set_src_div,
+ .get_src_div = rcg_get_src_div,
+ .is_enabled = rcg_is_enabled,
+ .list_registers = rcg_list_registers,
+};
+
+static void *cbc_dt_parser(struct device *dev, struct device_node *np)
+{
+ struct msmclk_data *drv;
+ struct branch_clk *branch_clk;
+ u32 rc;
+
+ branch_clk = devm_kzalloc(dev, sizeof(*branch_clk), GFP_KERNEL);
+ if (!branch_clk) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ drv = msmclk_parse_phandle(dev, np->parent->phandle);
+ if (IS_ERR_OR_NULL(drv))
+ return ERR_CAST(drv);
+ branch_clk->base = &drv->base;
+
+ rc = of_property_read_u32(np, "qcom,base-offset",
+ &branch_clk->cbcr_reg);
+ if (rc) {
+ dt_err(np, "missing/incorrect qcom,base-offset dt property\n");
+ return ERR_PTR(rc);
+ }
+
+ /* Optional property */
+ of_property_read_u32(np, "qcom,bcr-offset", &branch_clk->bcr_reg);
+
+ branch_clk->has_sibling = of_property_read_bool(np,
+ "qcom,has-sibling");
+
+ branch_clk->c.ops = &clk_ops_branch;
+
+ return msmclk_generic_clk_init(dev, np, &branch_clk->c);
+}
+MSMCLK_PARSER(cbc_dt_parser, "qcom,cbc", 0);
+
+static void *local_vote_clk_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct local_vote_clk *vote_clk;
+ struct msmclk_data *drv;
+ int rc, val;
+
+ vote_clk = devm_kzalloc(dev, sizeof(*vote_clk), GFP_KERNEL);
+ if (!vote_clk) {
+ dt_err(np, "failed to alloc memory\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ drv = msmclk_parse_phandle(dev, np->parent->phandle);
+ if (IS_ERR_OR_NULL(drv))
+ return ERR_CAST(drv);
+ vote_clk->base = &drv->base;
+
+ rc = of_property_read_u32(np, "qcom,base-offset",
+ &vote_clk->cbcr_reg);
+ if (rc) {
+ dt_err(np, "missing/incorrect qcom,base-offset dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,en-offset", &vote_clk->vote_reg);
+ if (rc) {
+ dt_err(np, "missing/incorrect qcom,en-offset dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,en-bit", &val);
+ if (rc) {
+ dt_err(np, "missing/incorrect qcom,en-bit dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+ vote_clk->en_mask = BIT(val);
+
+ vote_clk->c.ops = &clk_ops_vote;
+
+ /* Optional property */
+ of_property_read_u32(np, "qcom,bcr-offset", &vote_clk->bcr_reg);
+
+ return msmclk_generic_clk_init(dev, np, &vote_clk->c);
+}
+MSMCLK_PARSER(local_vote_clk_dt_parser, "qcom,local-vote-clk", 0);
+
+static void *gate_clk_dt_parser(struct device *dev, struct device_node *np)
+{
+ struct gate_clk *gate_clk;
+ struct msmclk_data *drv;
+ u32 en_bit, rc;
+
+ gate_clk = devm_kzalloc(dev, sizeof(*gate_clk), GFP_KERNEL);
+ if (!gate_clk) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ drv = msmclk_parse_phandle(dev, np->parent->phandle);
+ if (IS_ERR_OR_NULL(drv))
+ return ERR_CAST(drv);
+ gate_clk->base = &drv->base;
+
+ rc = of_property_read_u32(np, "qcom,en-offset", &gate_clk->en_reg);
+ if (rc) {
+ dt_err(np, "missing qcom,en-offset dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,en-bit", &en_bit);
+ if (rc) {
+ dt_err(np, "missing qcom,en-bit dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+ gate_clk->en_mask = BIT(en_bit);
+
+ /* Optional Property */
+ rc = of_property_read_u32(np, "qcom,delay", &gate_clk->delay_us);
+ if (rc)
+ gate_clk->delay_us = 0;
+
+ gate_clk->c.ops = &clk_ops_gate;
+ return msmclk_generic_clk_init(dev, np, &gate_clk->c);
+}
+MSMCLK_PARSER(gate_clk_dt_parser, "qcom,gate-clk", 0);
+
+
+static inline u32 rcg_calc_m(u32 m, u32 n)
+{
+ return m;
+}
+
+static inline u32 rcg_calc_n(u32 m, u32 n)
+{
+ n = n > 1 ? n : 0;
+ return ~((n)-(m)) * !!(n);
+}
+
+static inline u32 rcg_calc_duty_cycle(u32 m, u32 n)
+{
+ return ~n;
+}
+
+static inline u32 rcg_calc_div_src(u32 div_int, u32 div_frac, u32 src_sel)
+{
+ int div = 2 * div_int + (div_frac ? 1 : 0) - 1;
+ /* set bypass mode instead of a divider of 1 */
+ div = (div != 1) ? div : 0;
+ return BVAL(4, 0, max(div, 0))
+ | BVAL(10, 8, src_sel);
+}
+
+struct clk_src *msmclk_parse_clk_src(struct device *dev,
+ struct device_node *np, int *array_size)
+{
+ struct clk_src *clks;
+ const void *prop;
+ int num_parents, len, i, prop_len, rc;
+ char *name = "qcom,parents";
+
+ if (!array_size) {
+ dt_err(np, "array_size must be a valid pointer\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ prop = of_get_property(np, name, &prop_len);
+ if (!prop) {
+ dt_prop_err(np, name, "missing dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ len = sizeof(phandle) + sizeof(u32);
+ if (prop_len % len) {
+ dt_prop_err(np, name, "invalid property length\n");
+ return ERR_PTR(-EINVAL);
+ }
+ num_parents = prop_len / len;
+
+ clks = devm_kzalloc(dev, sizeof(*clks) * num_parents, GFP_KERNEL);
+ if (!clks) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Assume that u32 and phandle have the same size */
+ for (i = 0; i < num_parents; i++) {
+ phandle p;
+ struct clk_src *a = &clks[i];
+
+ rc = of_property_read_u32_index(np, name, 2 * i, &a->sel);
+ rc |= of_property_read_phandle_index(np, name, 2 * i + 1, &p);
+
+ if (rc) {
+ dt_prop_err(np, name,
+ "unable to read parent clock or mux index\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ a->src = msmclk_parse_phandle(dev, p);
+ if (IS_ERR(a->src)) {
+ dt_prop_err(np, name, "hashtable lookup failed\n");
+ return ERR_CAST(a->src);
+ }
+ }
+
+ *array_size = num_parents;
+
+ return clks;
+}
+
+static int rcg_parse_freq_tbl(struct device *dev,
+ struct device_node *np, struct rcg_clk *rcg)
+{
+ const void *prop;
+ u32 prop_len, num_rows, i, j = 0;
+ struct clk_freq_tbl *tbl;
+ int rc;
+ char *name = "qcom,freq-tbl";
+
+ prop = of_get_property(np, name, &prop_len);
+ if (!prop) {
+ dt_prop_err(np, name, "missing dt property\n");
+ return -EINVAL;
+ }
+
+ prop_len /= sizeof(u32);
+ if (prop_len % 6) {
+ dt_prop_err(np, name, "bad length\n");
+ return -EINVAL;
+ }
+
+ num_rows = prop_len / 6;
+ /* Array is null terminated. */
+ rcg->freq_tbl = devm_kzalloc(dev,
+ sizeof(*rcg->freq_tbl) * (num_rows + 1),
+ GFP_KERNEL);
+
+ if (!rcg->freq_tbl) {
+ dt_err(np, "memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ tbl = rcg->freq_tbl;
+ for (i = 0; i < num_rows; i++, tbl++) {
+ phandle p;
+ u32 div_int, div_frac, m, n, src_sel, freq_hz;
+
+ rc = of_property_read_u32_index(np, name, j++, &freq_hz);
+ rc |= of_property_read_u32_index(np, name, j++, &div_int);
+ rc |= of_property_read_u32_index(np, name, j++, &div_frac);
+ rc |= of_property_read_u32_index(np, name, j++, &m);
+ rc |= of_property_read_u32_index(np, name, j++, &n);
+ rc |= of_property_read_u32_index(np, name, j++, &p);
+
+ if (rc) {
+ dt_prop_err(np, name, "unable to read u32\n");
+ return -EINVAL;
+ }
+
+ tbl->freq_hz = (unsigned long)freq_hz;
+ tbl->src_clk = msmclk_parse_phandle(dev, p);
+ if (IS_ERR_OR_NULL(tbl->src_clk)) {
+ dt_prop_err(np, name, "hashtable lookup failure\n");
+ return PTR_ERR(tbl->src_clk);
+ }
+
+ tbl->m_val = rcg_calc_m(m, n);
+ tbl->n_val = rcg_calc_n(m, n);
+ tbl->d_val = rcg_calc_duty_cycle(m, n);
+
+ src_sel = parent_to_src_sel(rcg->c.parents,
+ rcg->c.num_parents, tbl->src_clk);
+ tbl->div_src_val = rcg_calc_div_src(div_int, div_frac,
+ src_sel);
+ }
+ /* End table with special value */
+ tbl->freq_hz = FREQ_END;
+ return 0;
+}
+
+static void *rcg_clk_dt_parser(struct device *dev, struct device_node *np)
+{
+ struct rcg_clk *rcg;
+ struct msmclk_data *drv;
+ int rc;
+
+ rcg = devm_kzalloc(dev, sizeof(*rcg), GFP_KERNEL);
+ if (!rcg) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ drv = msmclk_parse_phandle(dev, np->parent->phandle);
+ if (IS_ERR_OR_NULL(drv))
+ return drv;
+ rcg->base = &drv->base;
+
+ rcg->c.parents = msmclk_parse_clk_src(dev, np, &rcg->c.num_parents);
+ if (IS_ERR(rcg->c.parents)) {
+ dt_err(np, "unable to read parents\n");
+ return ERR_CAST(rcg->c.parents);
+ }
+
+ rc = of_property_read_u32(np, "qcom,base-offset", &rcg->cmd_rcgr_reg);
+ if (rc) {
+ dt_err(np, "missing qcom,base-offset dt property\n");
+ return ERR_PTR(rc);
+ }
+
+ rc = rcg_parse_freq_tbl(dev, np, rcg);
+ if (rc) {
+ dt_err(np, "unable to read freq_tbl\n");
+ return ERR_PTR(rc);
+ }
+ rcg->current_freq = &rcg_dummy_freq;
+
+ if (of_device_is_compatible(np, "qcom,rcg-hid")) {
+ rcg->c.ops = &clk_ops_rcg;
+ rcg->set_rate = set_rate_hid;
+ } else if (of_device_is_compatible(np, "qcom,rcg-mn")) {
+ rcg->c.ops = &clk_ops_rcg_mnd;
+ rcg->set_rate = set_rate_mnd;
+ } else {
+ dt_err(np, "unexpected compatible string\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return msmclk_generic_clk_init(dev, np, &rcg->c);
+}
+MSMCLK_PARSER(rcg_clk_dt_parser, "qcom,rcg-hid", 0);
+MSMCLK_PARSER(rcg_clk_dt_parser, "qcom,rcg-mn", 1);
+
+static int parse_rec_parents(struct device *dev,
+ struct device_node *np, struct mux_clk *mux)
+{
+ int i, rc;
+ char *name = "qcom,recursive-parents";
+ phandle p;
+
+ mux->num_rec_parents = of_property_count_phandles(np, name);
+ if (mux->num_rec_parents <= 0)
+ return 0;
+
+ mux->rec_parents = devm_kzalloc(dev,
+ sizeof(*mux->rec_parents) * mux->num_rec_parents,
+ GFP_KERNEL);
+
+ if (!mux->rec_parents) {
+ dt_err(np, "memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < mux->num_rec_parents; i++) {
+ rc = of_property_read_phandle_index(np, name, i, &p);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read u32\n");
+ return rc;
+ }
+
+ mux->rec_parents[i] = msmclk_parse_phandle(dev, p);
+ if (IS_ERR(mux->rec_parents[i])) {
+ dt_prop_err(np, name, "hashtable lookup failure\n");
+ return PTR_ERR(mux->rec_parents[i]);
+ }
+ }
+
+ return 0;
+}
+
+static void *mux_reg_clk_dt_parser(struct device *dev, struct device_node *np)
+{
+ struct mux_clk *mux;
+ struct msmclk_data *drv;
+ int rc;
+
+ mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mux->parents = msmclk_parse_clk_src(dev, np, &mux->num_parents);
+ if (IS_ERR(mux->parents))
+ return mux->parents;
+
+ mux->c.parents = mux->parents;
+ mux->c.num_parents = mux->num_parents;
+
+ drv = msmclk_parse_phandle(dev, np->parent->phandle);
+ if (IS_ERR_OR_NULL(drv))
+ return drv;
+ mux->base = &drv->base;
+
+ rc = parse_rec_parents(dev, np, mux);
+ if (rc) {
+ dt_err(np, "Incorrect qcom,recursive-parents dt property\n");
+ return ERR_PTR(rc);
+ }
+
+ rc = of_property_read_u32(np, "qcom,offset", &mux->offset);
+ if (rc) {
+ dt_err(np, "missing qcom,offset dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,mask", &mux->mask);
+ if (rc) {
+ dt_err(np, "missing qcom,mask dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,shift", &mux->shift);
+ if (rc) {
+ dt_err(np, "missing qcom,shift dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ mux->c.ops = &clk_ops_gen_mux;
+ mux->ops = &mux_reg_ops;
+
+ /* Optional Properties */
+ of_property_read_u32(np, "qcom,en-offset", &mux->en_offset);
+ of_property_read_u32(np, "qcom,en-mask", &mux->en_mask);
+
+ return msmclk_generic_clk_init(dev, np, &mux->c);
+};
+MSMCLK_PARSER(mux_reg_clk_dt_parser, "qcom,mux-reg", 0);
+
+static void *measure_clk_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct mux_clk *mux;
+ struct clk *c;
+ struct measure_clk_data *p;
+ struct clk_ops *clk_ops_measure_mux;
+ phandle cxo;
+ int rc;
+
+ c = mux_reg_clk_dt_parser(dev, np);
+ if (IS_ERR(c))
+ return c;
+
+ mux = to_mux_clk(c);
+
+ p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rc = of_property_read_phandle_index(np, "qcom,cxo", 0, &cxo);
+ if (rc) {
+ dt_err(np, "missing qcom,cxo\n");
+ return ERR_PTR(-EINVAL);
+ }
+ p->cxo = msmclk_parse_phandle(dev, cxo);
+ if (IS_ERR_OR_NULL(p->cxo)) {
+ dt_prop_err(np, "qcom,cxo", "hashtable lookup failure\n");
+ return p->cxo;
+ }
+
+ rc = of_property_read_u32(np, "qcom,xo-div4-cbcr", &p->xo_div4_cbcr);
+ if (rc) {
+ dt_err(np, "missing qcom,xo-div4-cbcr dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,test-pad-config", &p->plltest_val);
+ if (rc) {
+ dt_err(np, "missing qcom,test-pad-config dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ p->base = mux->base;
+ p->ctl_reg = mux->offset + 0x4;
+ p->status_reg = mux->offset + 0x8;
+ p->plltest_reg = mux->offset + 0xC;
+ mux->priv = p;
+
+ clk_ops_measure_mux = devm_kzalloc(dev, sizeof(*clk_ops_measure_mux),
+ GFP_KERNEL);
+ if (!clk_ops_measure_mux) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ *clk_ops_measure_mux = clk_ops_gen_mux;
+ clk_ops_measure_mux->get_rate = measure_get_rate;
+
+ mux->c.ops = clk_ops_measure_mux;
+
+ /* Already did generic clk init */
+ return &mux->c;
+};
+MSMCLK_PARSER(measure_clk_dt_parser, "qcom,measure-mux", 0);
+
+static void *div_clk_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct div_clk *div_clk;
+ struct msmclk_data *drv;
+ int rc;
+
+ div_clk = devm_kzalloc(dev, sizeof(*div_clk), GFP_KERNEL);
+ if (!div_clk) {
+ dt_err(np, "memory alloc failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rc = of_property_read_u32(np, "qcom,max-div", &div_clk->data.max_div);
+ if (rc) {
+ dt_err(np, "missing qcom,max-div\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,min-div", &div_clk->data.min_div);
+ if (rc) {
+ dt_err(np, "missing qcom,min-div\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,base-offset", &div_clk->offset);
+ if (rc) {
+ dt_err(np, "missing qcom,base-offset\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,mask", &div_clk->mask);
+ if (rc) {
+ dt_err(np, "missing qcom,mask\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,shift", &div_clk->shift);
+ if (rc) {
+ dt_err(np, "missing qcom,shift\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (of_property_read_bool(np, "qcom,slave-div"))
+ div_clk->c.ops = &clk_ops_slave_div;
+ else
+ div_clk->c.ops = &clk_ops_div;
+ div_clk->ops = &div_reg_ops;
+
+ drv = msmclk_parse_phandle(dev, np->parent->phandle);
+ if (IS_ERR_OR_NULL(drv))
+ return ERR_CAST(drv);
+ div_clk->base = &drv->base;
+
+ return msmclk_generic_clk_init(dev, np, &div_clk->c);
+};
+MSMCLK_PARSER(div_clk_dt_parser, "qcom,div-clk", 0);
+
+static void *fixed_div_clk_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct div_clk *div_clk;
+ int rc;
+
+ div_clk = devm_kzalloc(dev, sizeof(*div_clk), GFP_KERNEL);
+ if (!div_clk) {
+ dt_err(np, "memory alloc failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rc = of_property_read_u32(np, "qcom,div", &div_clk->data.div);
+ if (rc) {
+ dt_err(np, "missing qcom,div\n");
+ return ERR_PTR(-EINVAL);
+ }
+ div_clk->data.min_div = div_clk->data.div;
+ div_clk->data.max_div = div_clk->data.div;
+
+ if (of_property_read_bool(np, "qcom,slave-div"))
+ div_clk->c.ops = &clk_ops_slave_div;
+ else
+ div_clk->c.ops = &clk_ops_div;
+ div_clk->ops = &div_reg_ops;
+
+ return msmclk_generic_clk_init(dev, np, &div_clk->c);
+}
+MSMCLK_PARSER(fixed_div_clk_dt_parser, "qcom,fixed-div-clk", 0);
+
+static void *reset_clk_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct reset_clk *reset_clk;
+ struct msmclk_data *drv;
+ int rc;
+
+ reset_clk = devm_kzalloc(dev, sizeof(*reset_clk), GFP_KERNEL);
+ if (!reset_clk) {
+ dt_err(np, "memory alloc failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rc = of_property_read_u32(np, "qcom,base-offset",
+ &reset_clk->reset_reg);
+ if (rc) {
+ dt_err(np, "missing qcom,base-offset\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ drv = msmclk_parse_phandle(dev, np->parent->phandle);
+ if (IS_ERR_OR_NULL(drv))
+ return ERR_CAST(drv);
+ reset_clk->base = &drv->base;
+
+ reset_clk->c.ops = &clk_ops_rst;
+ return msmclk_generic_clk_init(dev, np, &reset_clk->c);
+};
+MSMCLK_PARSER(reset_clk_dt_parser, "qcom,reset-clk", 0);
diff --git a/drivers/clk/msm/clock-pll.c b/drivers/clk/msm/clock-pll.c
new file mode 100644
index 000000000000..4e746b9754af
--- /dev/null
+++ b/drivers/clk/msm/clock-pll.c
@@ -0,0 +1,1205 @@
+/*
+ * Copyright (c) 2012-2015, 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/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <soc/qcom/clock-pll.h>
+#include <soc/qcom/msm-clock-controller.h>
+
+#include "clock.h"
+
+#define PLL_OUTCTRL BIT(0)
+#define PLL_BYPASSNL BIT(1)
+#define PLL_RESET_N BIT(2)
+#define PLL_MODE_MASK BM(3, 0)
+
+#define PLL_EN_REG(x) (*(x)->base + (unsigned long) (x)->en_reg)
+#define PLL_STATUS_REG(x) (*(x)->base + (unsigned long) (x)->status_reg)
+#define PLL_ALT_STATUS_REG(x) (*(x)->base + (unsigned long) \
+ (x)->alt_status_reg)
+#define PLL_MODE_REG(x) (*(x)->base + (unsigned long) (x)->mode_reg)
+#define PLL_L_REG(x) (*(x)->base + (unsigned long) (x)->l_reg)
+#define PLL_M_REG(x) (*(x)->base + (unsigned long) (x)->m_reg)
+#define PLL_N_REG(x) (*(x)->base + (unsigned long) (x)->n_reg)
+#define PLL_CONFIG_REG(x) (*(x)->base + (unsigned long) (x)->config_reg)
+#define PLL_ALPHA_REG(x) (*(x)->base + (unsigned long) (x)->alpha_reg)
+#define PLL_CFG_ALT_REG(x) (*(x)->base + (unsigned long) \
+ (x)->config_alt_reg)
+#define PLL_CFG_CTL_REG(x) (*(x)->base + (unsigned long) \
+ (x)->config_ctl_reg)
+#define PLL_CFG_CTL_HI_REG(x) (*(x)->base + (unsigned long) \
+ (x)->config_ctl_hi_reg)
+#define PLL_TEST_CTL_LO_REG(x) (*(x)->base + (unsigned long) \
+ (x)->test_ctl_lo_reg)
+#define PLL_TEST_CTL_HI_REG(x) (*(x)->base + (unsigned long) \
+ (x)->test_ctl_hi_reg)
+static DEFINE_SPINLOCK(pll_reg_lock);
+
+#define ENABLE_WAIT_MAX_LOOPS 200
+#define PLL_LOCKED_BIT BIT(16)
+
+#define SPM_FORCE_EVENT 0x4
+
+static int pll_vote_clk_enable(struct clk *c)
+{
+ u32 ena, count;
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+ ena = readl_relaxed(PLL_EN_REG(pllv));
+ ena |= pllv->en_mask;
+ writel_relaxed(ena, PLL_EN_REG(pllv));
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+
+ /*
+ * Use a memory barrier since some PLL status registers are
+ * not within the same 1K segment as the voting registers.
+ */
+ mb();
+
+ /* Wait for pll to enable. */
+ for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
+ if (readl_relaxed(PLL_STATUS_REG(pllv)) & pllv->status_mask)
+ return 0;
+ udelay(1);
+ }
+
+ WARN("PLL %s didn't enable after voting for it!\n", c->dbg_name);
+
+ return -ETIMEDOUT;
+}
+
+static void pll_vote_clk_disable(struct clk *c)
+{
+ u32 ena;
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+ ena = readl_relaxed(PLL_EN_REG(pllv));
+ ena &= ~(pllv->en_mask);
+ writel_relaxed(ena, PLL_EN_REG(pllv));
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+}
+
+static int pll_vote_clk_is_enabled(struct clk *c)
+{
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+ return !!(readl_relaxed(PLL_STATUS_REG(pllv)) & pllv->status_mask);
+}
+
+static enum handoff pll_vote_clk_handoff(struct clk *c)
+{
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+ if (readl_relaxed(PLL_EN_REG(pllv)) & pllv->en_mask)
+ return HANDOFF_ENABLED_CLK;
+
+ return HANDOFF_DISABLED_CLK;
+}
+
+static void __iomem *pll_vote_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+ static struct clk_register_data data1[] = {
+ {"APPS_VOTE", 0x0},
+ };
+
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data1;
+ *size = ARRAY_SIZE(data1);
+ return PLL_EN_REG(pllv);
+}
+
+struct clk_ops clk_ops_pll_vote = {
+ .enable = pll_vote_clk_enable,
+ .disable = pll_vote_clk_disable,
+ .is_enabled = pll_vote_clk_is_enabled,
+ .handoff = pll_vote_clk_handoff,
+ .list_registers = pll_vote_clk_list_registers,
+};
+
+/*
+ * spm_event() -- Set/Clear SPM events
+ * PLL off sequence -- enable (1)
+ * Set L2_SPM_FORCE_EVENT_EN[bit] register to 1
+ * Set L2_SPM_FORCE_EVENT[bit] register to 1
+ * PLL on sequence -- enable (0)
+ * Clear L2_SPM_FORCE_EVENT[bit] register to 0
+ * Clear L2_SPM_FORCE_EVENT_EN[bit] register to 0
+ */
+static void spm_event(void __iomem *base, u32 offset, u32 bit,
+ bool enable)
+{
+ uint32_t val;
+
+ if (!base)
+ return;
+
+ if (enable) {
+ /* L2_SPM_FORCE_EVENT_EN */
+ val = readl_relaxed(base + offset);
+ val |= BIT(bit);
+ writel_relaxed(val, (base + offset));
+ /* Ensure that the write above goes through. */
+ mb();
+
+ /* L2_SPM_FORCE_EVENT */
+ val = readl_relaxed(base + offset + SPM_FORCE_EVENT);
+ val |= BIT(bit);
+ writel_relaxed(val, (base + offset + SPM_FORCE_EVENT));
+ /* Ensure that the write above goes through. */
+ mb();
+ } else {
+ /* L2_SPM_FORCE_EVENT */
+ val = readl_relaxed(base + offset + SPM_FORCE_EVENT);
+ val &= ~BIT(bit);
+ writel_relaxed(val, (base + offset + SPM_FORCE_EVENT));
+ /* Ensure that the write above goes through. */
+ mb();
+
+ /* L2_SPM_FORCE_EVENT_EN */
+ val = readl_relaxed(base + offset);
+ val &= ~BIT(bit);
+ writel_relaxed(val, (base + offset));
+ /* Ensure that the write above goes through. */
+ mb();
+ }
+}
+
+static void __pll_config_reg(void __iomem *pll_config, struct pll_freq_tbl *f,
+ struct pll_config_masks *masks)
+{
+ u32 regval;
+
+ regval = readl_relaxed(pll_config);
+
+ /* Enable the MN counter if used */
+ if (f->m_val)
+ regval |= masks->mn_en_mask;
+
+ /* Set pre-divider and post-divider values */
+ regval &= ~masks->pre_div_mask;
+ regval |= f->pre_div_val;
+ regval &= ~masks->post_div_mask;
+ regval |= f->post_div_val;
+
+ /* Select VCO setting */
+ regval &= ~masks->vco_mask;
+ regval |= f->vco_val;
+
+ /* Enable main output if it has not been enabled */
+ if (masks->main_output_mask && !(regval & masks->main_output_mask))
+ regval |= masks->main_output_mask;
+
+ writel_relaxed(regval, pll_config);
+}
+
+static int sr2_pll_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_clk *pll = to_pll_clk(c);
+ int ret = 0, count;
+ u32 mode = readl_relaxed(PLL_MODE_REG(pll));
+ u32 lockmask = pll->masks.lock_mask ?: PLL_LOCKED_BIT;
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+
+ spm_event(pll->spm_ctrl.spm_base, pll->spm_ctrl.offset,
+ pll->spm_ctrl.event_bit, false);
+
+ /* Disable PLL bypass mode. */
+ mode |= PLL_BYPASSNL;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset. Delay 10us just to be safe.
+ */
+ mb();
+ udelay(10);
+
+ /* De-assert active-low PLL reset. */
+ mode |= PLL_RESET_N;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /* Wait for pll to lock. */
+ for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
+ if (readl_relaxed(PLL_STATUS_REG(pll)) & lockmask)
+ break;
+ udelay(1);
+ }
+
+ if (!(readl_relaxed(PLL_STATUS_REG(pll)) & lockmask))
+ pr_err("PLL %s didn't lock after enabling it!\n", c->dbg_name);
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /* Ensure that the write above goes through before returning. */
+ mb();
+
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+ return ret;
+}
+
+void __variable_rate_pll_init(struct clk *c)
+{
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 regval;
+
+ regval = readl_relaxed(PLL_CONFIG_REG(pll));
+
+ if (pll->masks.post_div_mask) {
+ regval &= ~pll->masks.post_div_mask;
+ regval |= pll->vals.post_div_masked;
+ }
+
+ if (pll->masks.pre_div_mask) {
+ regval &= ~pll->masks.pre_div_mask;
+ regval |= pll->vals.pre_div_masked;
+ }
+
+ if (pll->masks.main_output_mask)
+ regval |= pll->masks.main_output_mask;
+
+ if (pll->masks.early_output_mask)
+ regval |= pll->masks.early_output_mask;
+
+ if (pll->vals.enable_mn)
+ regval |= pll->masks.mn_en_mask;
+ else
+ regval &= ~pll->masks.mn_en_mask;
+
+ writel_relaxed(regval, PLL_CONFIG_REG(pll));
+
+ regval = readl_relaxed(PLL_MODE_REG(pll));
+ if (pll->masks.apc_pdn_mask)
+ regval &= ~pll->masks.apc_pdn_mask;
+ writel_relaxed(regval, PLL_MODE_REG(pll));
+
+ writel_relaxed(pll->vals.alpha_val, PLL_ALPHA_REG(pll));
+ writel_relaxed(pll->vals.config_ctl_val, PLL_CFG_CTL_REG(pll));
+ if (pll->vals.config_ctl_hi_val)
+ writel_relaxed(pll->vals.config_ctl_hi_val,
+ PLL_CFG_CTL_HI_REG(pll));
+ if (pll->init_test_ctl) {
+ writel_relaxed(pll->vals.test_ctl_lo_val,
+ PLL_TEST_CTL_LO_REG(pll));
+ writel_relaxed(pll->vals.test_ctl_hi_val,
+ PLL_TEST_CTL_HI_REG(pll));
+ }
+
+ pll->inited = true;
+}
+
+static int variable_rate_pll_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_clk *pll = to_pll_clk(c);
+ int ret = 0, count;
+ u32 mode, testlo;
+ u32 lockmask = pll->masks.lock_mask ?: PLL_LOCKED_BIT;
+ u32 mode_lock;
+ u64 time;
+ bool early_lock = false;
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+
+ if (unlikely(!to_pll_clk(c)->inited))
+ __variable_rate_pll_init(c);
+
+ mode = readl_relaxed(PLL_MODE_REG(pll));
+
+ /* Set test control bits as required by HW doc */
+ if (pll->test_ctl_lo_reg && pll->vals.test_ctl_lo_val &&
+ pll->pgm_test_ctl_enable)
+ writel_relaxed(pll->vals.test_ctl_lo_val,
+ PLL_TEST_CTL_LO_REG(pll));
+
+ /* Enable test_ctl debug */
+ mode |= BIT(3);
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
+ testlo &= ~BM(7, 6);
+ testlo |= 0xC0;
+ writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
+ /* Wait for the write to complete */
+ mb();
+
+ /* Disable PLL bypass mode. */
+ mode |= PLL_BYPASSNL;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset. Use 10us to be sure.
+ */
+ mb();
+ udelay(10);
+
+ /* De-assert active-low PLL reset. */
+ mode |= PLL_RESET_N;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /*
+ * 5us delay mandated by HPG. However, put in a 200us delay here.
+ * This is to address possible locking issues with the PLL exhibit
+ * early "transient" locks about 16us from this point. With this
+ * higher delay, we avoid running into those transients.
+ */
+ mb();
+ udelay(200);
+
+ /* Clear test control bits */
+ if (pll->test_ctl_lo_reg && pll->vals.test_ctl_lo_val &&
+ pll->pgm_test_ctl_enable)
+ writel_relaxed(0x0, PLL_TEST_CTL_LO_REG(pll));
+
+
+ time = sched_clock();
+ /* Wait for pll to lock. */
+ for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
+ if (readl_relaxed(PLL_STATUS_REG(pll)) & lockmask) {
+ udelay(1);
+ /*
+ * Check again to be sure. This is to avoid
+ * breaking too early if there is a "transient"
+ * lock.
+ */
+ if ((readl_relaxed(PLL_STATUS_REG(pll)) & lockmask))
+ break;
+ else
+ early_lock = true;
+ }
+ udelay(1);
+ }
+ time = sched_clock() - time;
+
+ mode_lock = readl_relaxed(PLL_STATUS_REG(pll));
+
+ if (!(mode_lock & lockmask)) {
+ pr_err("PLL lock bit detection total wait time: %lld ns", time);
+ pr_err("PLL %s didn't lock after enabling for L value 0x%x!\n",
+ c->dbg_name, readl_relaxed(PLL_L_REG(pll)));
+ pr_err("mode register is 0x%x\n",
+ readl_relaxed(PLL_STATUS_REG(pll)));
+ pr_err("user control register is 0x%x\n",
+ readl_relaxed(PLL_CONFIG_REG(pll)));
+ pr_err("config control register is 0x%x\n",
+ readl_relaxed(PLL_CFG_CTL_REG(pll)));
+ pr_err("test control high register is 0x%x\n",
+ readl_relaxed(PLL_TEST_CTL_HI_REG(pll)));
+ pr_err("test control low register is 0x%x\n",
+ readl_relaxed(PLL_TEST_CTL_LO_REG(pll)));
+ pr_err("early lock? %s\n", early_lock ? "yes" : "no");
+
+ testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
+ testlo &= ~BM(7, 6);
+ writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
+ /* Wait for the write to complete */
+ mb();
+
+ pr_err("test_ctl_lo = 0x%x, pll status is: 0x%x\n",
+ readl_relaxed(PLL_TEST_CTL_LO_REG(pll)),
+ readl_relaxed(PLL_ALT_STATUS_REG(pll)));
+
+ testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
+ testlo &= ~BM(7, 6);
+ testlo |= 0x40;
+ writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
+ /* Wait for the write to complete */
+ mb();
+ pr_err("test_ctl_lo = 0x%x, pll status is: 0x%x\n",
+ readl_relaxed(PLL_TEST_CTL_LO_REG(pll)),
+ readl_relaxed(PLL_ALT_STATUS_REG(pll)));
+
+ testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
+ testlo &= ~BM(7, 6);
+ testlo |= 0x80;
+ writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
+ /* Wait for the write to complete */
+ mb();
+
+ pr_err("test_ctl_lo = 0x%x, pll status is: 0x%x\n",
+ readl_relaxed(PLL_TEST_CTL_LO_REG(pll)),
+ readl_relaxed(PLL_ALT_STATUS_REG(pll)));
+
+ testlo = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
+ testlo &= ~BM(7, 6);
+ testlo |= 0xC0;
+ writel_relaxed(testlo, PLL_TEST_CTL_LO_REG(pll));
+ /* Wait for the write to complete */
+ mb();
+
+ pr_err("test_ctl_lo = 0x%x, pll status is: 0x%x\n",
+ readl_relaxed(PLL_TEST_CTL_LO_REG(pll)),
+ readl_relaxed(PLL_ALT_STATUS_REG(pll)));
+ panic("failed to lock %s PLL\n", c->dbg_name);
+ }
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /* Ensure that the write above goes through before returning. */
+ mb();
+
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+
+ return ret;
+}
+
+static void variable_rate_pll_clk_disable_hwfsm(struct clk *c)
+{
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 regval;
+
+ /* Set test control bit to stay-in-CFA if necessary */
+ if (pll->test_ctl_lo_reg && pll->pgm_test_ctl_enable) {
+ regval = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
+ writel_relaxed(regval | BIT(16),
+ PLL_TEST_CTL_LO_REG(pll));
+ }
+
+ /* 8 reference clock cycle delay mandated by the HPG */
+ udelay(1);
+}
+
+static int variable_rate_pll_clk_enable_hwfsm(struct clk *c)
+{
+ struct pll_clk *pll = to_pll_clk(c);
+ int count;
+ u32 lockmask = pll->masks.lock_mask ?: PLL_LOCKED_BIT;
+ unsigned long flags;
+ u32 regval;
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+
+ /* Clear test control bit if necessary */
+ if (pll->test_ctl_lo_reg && pll->pgm_test_ctl_enable) {
+ regval = readl_relaxed(PLL_TEST_CTL_LO_REG(pll));
+ regval &= ~BIT(16);
+ writel_relaxed(regval, PLL_TEST_CTL_LO_REG(pll));
+ }
+
+ /* Wait for 50us explicitly to avoid transient locks */
+ udelay(50);
+
+ for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
+ if (readl_relaxed(PLL_STATUS_REG(pll)) & lockmask)
+ break;
+ udelay(1);
+ }
+
+ if (!(readl_relaxed(PLL_STATUS_REG(pll)) & lockmask))
+ pr_err("PLL %s didn't lock after enabling it!\n", c->dbg_name);
+
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+
+ return 0;
+}
+
+static void __pll_clk_enable_reg(void __iomem *mode_reg)
+{
+ u32 mode = readl_relaxed(mode_reg);
+ /* Disable PLL bypass mode. */
+ mode |= PLL_BYPASSNL;
+ writel_relaxed(mode, mode_reg);
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset. Delay 10us just to be safe.
+ */
+ mb();
+ udelay(10);
+
+ /* De-assert active-low PLL reset. */
+ mode |= PLL_RESET_N;
+ writel_relaxed(mode, mode_reg);
+
+ /* Wait until PLL is locked. */
+ mb();
+ udelay(50);
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, mode_reg);
+
+ /* Ensure that the write above goes through before returning. */
+ mb();
+}
+
+static int local_pll_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_clk *pll = to_pll_clk(c);
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+ __pll_clk_enable_reg(PLL_MODE_REG(pll));
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+
+ return 0;
+}
+
+static void __pll_clk_disable_reg(void __iomem *mode_reg)
+{
+ u32 mode = readl_relaxed(mode_reg);
+ mode &= ~PLL_MODE_MASK;
+ writel_relaxed(mode, mode_reg);
+}
+
+static void local_pll_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_clk *pll = to_pll_clk(c);
+
+ /*
+ * Disable the PLL output, disable test mode, enable
+ * the bypass mode, and assert the reset.
+ */
+ spin_lock_irqsave(&pll_reg_lock, flags);
+ spm_event(pll->spm_ctrl.spm_base, pll->spm_ctrl.offset,
+ pll->spm_ctrl.event_bit, true);
+ __pll_clk_disable_reg(PLL_MODE_REG(pll));
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+}
+
+static enum handoff local_pll_clk_handoff(struct clk *c)
+{
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 mode = readl_relaxed(PLL_MODE_REG(pll));
+ u32 mask = PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL;
+ unsigned long parent_rate;
+ u32 lval, mval, nval, userval;
+
+ if ((mode & mask) != mask)
+ return HANDOFF_DISABLED_CLK;
+
+ /* Assume bootloaders configure PLL to c->rate */
+ if (c->rate)
+ return HANDOFF_ENABLED_CLK;
+
+ parent_rate = clk_get_rate(c->parent);
+ lval = readl_relaxed(PLL_L_REG(pll));
+ mval = readl_relaxed(PLL_M_REG(pll));
+ nval = readl_relaxed(PLL_N_REG(pll));
+ userval = readl_relaxed(PLL_CONFIG_REG(pll));
+
+ c->rate = parent_rate * lval;
+
+ if (pll->masks.mn_en_mask && userval) {
+ if (!nval)
+ nval = 1;
+ c->rate += (parent_rate * mval) / nval;
+ }
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static long local_pll_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ struct pll_freq_tbl *nf;
+ struct pll_clk *pll = to_pll_clk(c);
+
+ if (!pll->freq_tbl)
+ return -EINVAL;
+
+ for (nf = pll->freq_tbl; nf->freq_hz != PLL_FREQ_END; nf++)
+ if (nf->freq_hz >= rate)
+ return nf->freq_hz;
+
+ nf--;
+ return nf->freq_hz;
+}
+
+static int local_pll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ struct pll_freq_tbl *nf;
+ struct pll_clk *pll = to_pll_clk(c);
+ unsigned long flags;
+
+ for (nf = pll->freq_tbl; nf->freq_hz != PLL_FREQ_END
+ && nf->freq_hz != rate; nf++)
+ ;
+
+ if (nf->freq_hz == PLL_FREQ_END)
+ return -EINVAL;
+
+ /*
+ * Ensure PLL is off before changing rate. For optimization reasons,
+ * assume no downstream clock is using actively using it.
+ */
+ spin_lock_irqsave(&c->lock, flags);
+ if (c->count)
+ c->ops->disable(c);
+
+ writel_relaxed(nf->l_val, PLL_L_REG(pll));
+ writel_relaxed(nf->m_val, PLL_M_REG(pll));
+ writel_relaxed(nf->n_val, PLL_N_REG(pll));
+
+ __pll_config_reg(PLL_CONFIG_REG(pll), nf, &pll->masks);
+
+ if (c->count)
+ c->ops->enable(c);
+
+ spin_unlock_irqrestore(&c->lock, flags);
+ return 0;
+}
+
+static enum handoff variable_rate_pll_handoff(struct clk *c)
+{
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 mode = readl_relaxed(PLL_MODE_REG(pll));
+ u32 mask = PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL;
+ u32 lval;
+
+ pll->src_rate = clk_get_rate(c->parent);
+
+ lval = readl_relaxed(PLL_L_REG(pll));
+ if (!lval)
+ return HANDOFF_DISABLED_CLK;
+
+ c->rate = pll->src_rate * lval;
+
+ if (c->rate > pll->max_rate || c->rate < pll->min_rate) {
+ WARN(1, "%s: Out of spec PLL", c->dbg_name);
+ return HANDOFF_DISABLED_CLK;
+ }
+
+ if ((mode & mask) != mask)
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+static long variable_rate_pll_round_rate(struct clk *c, unsigned long rate)
+{
+ struct pll_clk *pll = to_pll_clk(c);
+
+ if (!pll->src_rate)
+ return 0;
+
+ if (pll->no_prepared_reconfig && c->prepare_count && c->rate != rate)
+ return -EINVAL;
+
+ if (rate < pll->min_rate)
+ rate = pll->min_rate;
+ if (rate > pll->max_rate)
+ rate = pll->max_rate;
+
+ return min(pll->max_rate,
+ DIV_ROUND_UP(rate, pll->src_rate) * pll->src_rate);
+}
+
+/*
+ * For optimization reasons, assumes no downstream clocks are actively using
+ * it.
+ */
+static int variable_rate_pll_set_rate(struct clk *c, unsigned long rate)
+{
+ struct pll_clk *pll = to_pll_clk(c);
+ unsigned long flags;
+ u32 l_val;
+
+ if (rate != variable_rate_pll_round_rate(c, rate))
+ return -EINVAL;
+
+ l_val = rate / pll->src_rate;
+
+ spin_lock_irqsave(&c->lock, flags);
+
+ if (c->count && c->ops->disable)
+ c->ops->disable(c);
+
+ writel_relaxed(l_val, PLL_L_REG(pll));
+
+ if (c->count && c->ops->enable)
+ c->ops->enable(c);
+
+ spin_unlock_irqrestore(&c->lock, flags);
+
+ return 0;
+}
+
+int sr_pll_clk_enable(struct clk *c)
+{
+ u32 mode;
+ unsigned long flags;
+ struct pll_clk *pll = to_pll_clk(c);
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+ mode = readl_relaxed(PLL_MODE_REG(pll));
+ /* De-assert active-low PLL reset. */
+ mode |= PLL_RESET_N;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset. Delay 10us just to be safe.
+ */
+ mb();
+ udelay(10);
+
+ /* Disable PLL bypass mode. */
+ mode |= PLL_BYPASSNL;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /* Wait until PLL is locked. */
+ mb();
+ udelay(60);
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /* Ensure that the write above goes through before returning. */
+ mb();
+
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+
+ return 0;
+}
+
+int sr_hpm_lp_pll_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_clk *pll = to_pll_clk(c);
+ u32 count, mode;
+ int ret = 0;
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+
+ /* Disable PLL bypass mode and de-assert reset. */
+ mode = PLL_BYPASSNL | PLL_RESET_N;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /* Wait for pll to lock. */
+ for (count = ENABLE_WAIT_MAX_LOOPS; count > 0; count--) {
+ if (readl_relaxed(PLL_STATUS_REG(pll)) & PLL_LOCKED_BIT)
+ break;
+ udelay(1);
+ }
+
+ if (!(readl_relaxed(PLL_STATUS_REG(pll)) & PLL_LOCKED_BIT)) {
+ WARN("PLL %s didn't lock after enabling it!\n", c->dbg_name);
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ /* Enable PLL output. */
+ mode |= PLL_OUTCTRL;
+ writel_relaxed(mode, PLL_MODE_REG(pll));
+
+ /* Ensure the write above goes through before returning. */
+ mb();
+
+out:
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+ return ret;
+}
+
+
+static void __iomem *variable_rate_pll_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ struct pll_clk *pll = to_pll_clk(c);
+ static struct clk_register_data data[] = {
+ {"MODE", 0x0},
+ {"L", 0x4},
+ {"ALPHA", 0x8},
+ {"USER_CTL", 0x10},
+ {"CONFIG_CTL", 0x14},
+ {"STATUS", 0x1C},
+ };
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return PLL_MODE_REG(pll);
+}
+
+static void __iomem *local_pll_clk_list_registers(struct clk *c, int n,
+ struct clk_register_data **regs, u32 *size)
+{
+ /* Not compatible with 8960 & friends */
+ struct pll_clk *pll = to_pll_clk(c);
+ static struct clk_register_data data[] = {
+ {"MODE", 0x0},
+ {"L", 0x4},
+ {"M", 0x8},
+ {"N", 0xC},
+ {"USER", 0x10},
+ {"CONFIG", 0x14},
+ {"STATUS", 0x1C},
+ };
+ if (n)
+ return ERR_PTR(-EINVAL);
+
+ *regs = data;
+ *size = ARRAY_SIZE(data);
+ return PLL_MODE_REG(pll);
+}
+
+
+struct clk_ops clk_ops_local_pll = {
+ .enable = local_pll_clk_enable,
+ .disable = local_pll_clk_disable,
+ .set_rate = local_pll_clk_set_rate,
+ .handoff = local_pll_clk_handoff,
+ .list_registers = local_pll_clk_list_registers,
+};
+
+struct clk_ops clk_ops_sr2_pll = {
+ .enable = sr2_pll_clk_enable,
+ .disable = local_pll_clk_disable,
+ .set_rate = local_pll_clk_set_rate,
+ .round_rate = local_pll_clk_round_rate,
+ .handoff = local_pll_clk_handoff,
+ .list_registers = local_pll_clk_list_registers,
+};
+
+struct clk_ops clk_ops_variable_rate_pll_hwfsm = {
+ .enable = variable_rate_pll_clk_enable_hwfsm,
+ .disable = variable_rate_pll_clk_disable_hwfsm,
+ .set_rate = variable_rate_pll_set_rate,
+ .round_rate = variable_rate_pll_round_rate,
+ .handoff = variable_rate_pll_handoff,
+ .list_registers = variable_rate_pll_list_registers,
+};
+
+struct clk_ops clk_ops_variable_rate_pll = {
+ .enable = variable_rate_pll_clk_enable,
+ .disable = local_pll_clk_disable,
+ .set_rate = variable_rate_pll_set_rate,
+ .round_rate = variable_rate_pll_round_rate,
+ .handoff = variable_rate_pll_handoff,
+ .list_registers = variable_rate_pll_list_registers,
+};
+
+static DEFINE_SPINLOCK(soft_vote_lock);
+
+static int pll_acpu_vote_clk_enable(struct clk *c)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&soft_vote_lock, flags);
+
+ if (!*pllv->soft_vote)
+ ret = pll_vote_clk_enable(c);
+ if (ret == 0)
+ *pllv->soft_vote |= (pllv->soft_vote_mask);
+
+ spin_unlock_irqrestore(&soft_vote_lock, flags);
+ return ret;
+}
+
+static void pll_acpu_vote_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&soft_vote_lock, flags);
+
+ *pllv->soft_vote &= ~(pllv->soft_vote_mask);
+ if (!*pllv->soft_vote)
+ pll_vote_clk_disable(c);
+
+ spin_unlock_irqrestore(&soft_vote_lock, flags);
+}
+
+static enum handoff pll_acpu_vote_clk_handoff(struct clk *c)
+{
+ if (pll_vote_clk_handoff(c) == HANDOFF_DISABLED_CLK)
+ return HANDOFF_DISABLED_CLK;
+
+ if (pll_acpu_vote_clk_enable(c))
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+struct clk_ops clk_ops_pll_acpu_vote = {
+ .enable = pll_acpu_vote_clk_enable,
+ .disable = pll_acpu_vote_clk_disable,
+ .is_enabled = pll_vote_clk_is_enabled,
+ .handoff = pll_acpu_vote_clk_handoff,
+ .list_registers = pll_vote_clk_list_registers,
+};
+
+
+static int pll_sleep_clk_enable(struct clk *c)
+{
+ u32 ena;
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+ ena = readl_relaxed(PLL_EN_REG(pllv));
+ ena &= ~(pllv->en_mask);
+ writel_relaxed(ena, PLL_EN_REG(pllv));
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+ return 0;
+}
+
+static void pll_sleep_clk_disable(struct clk *c)
+{
+ u32 ena;
+ unsigned long flags;
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ spin_lock_irqsave(&pll_reg_lock, flags);
+ ena = readl_relaxed(PLL_EN_REG(pllv));
+ ena |= pllv->en_mask;
+ writel_relaxed(ena, PLL_EN_REG(pllv));
+ spin_unlock_irqrestore(&pll_reg_lock, flags);
+}
+
+static enum handoff pll_sleep_clk_handoff(struct clk *c)
+{
+ struct pll_vote_clk *pllv = to_pll_vote_clk(c);
+
+ if (!(readl_relaxed(PLL_EN_REG(pllv)) & pllv->en_mask))
+ return HANDOFF_ENABLED_CLK;
+
+ return HANDOFF_DISABLED_CLK;
+}
+
+/*
+ * This .ops is meant to be used by gpll0_sleep_clk_src. The aim is to utilise
+ * the h/w feature of sleep enable bit to denote if the PLL can be turned OFF
+ * once APPS goes to PC. gpll0_sleep_clk_src will be enabled only if there is a
+ * peripheral client using it and disabled if there is none. The current
+ * implementation of enable .ops clears the h/w bit of sleep enable while the
+ * disable .ops asserts it.
+ */
+
+struct clk_ops clk_ops_pll_sleep_vote = {
+ .enable = pll_sleep_clk_enable,
+ .disable = pll_sleep_clk_disable,
+ .handoff = pll_sleep_clk_handoff,
+ .list_registers = pll_vote_clk_list_registers,
+};
+
+static void __set_fsm_mode(void __iomem *mode_reg,
+ u32 bias_count, u32 lock_count)
+{
+ u32 regval = readl_relaxed(mode_reg);
+
+ /* De-assert reset to FSM */
+ regval &= ~BIT(21);
+ writel_relaxed(regval, mode_reg);
+
+ /* Program bias count */
+ regval &= ~BM(19, 14);
+ regval |= BVAL(19, 14, bias_count);
+ writel_relaxed(regval, mode_reg);
+
+ /* Program lock count */
+ regval &= ~BM(13, 8);
+ regval |= BVAL(13, 8, lock_count);
+ writel_relaxed(regval, mode_reg);
+
+ /* Enable PLL FSM voting */
+ regval |= BIT(20);
+ writel_relaxed(regval, mode_reg);
+}
+
+static void __configure_alt_config(struct pll_alt_config config,
+ struct pll_config_regs *regs)
+{
+ u32 regval;
+
+ regval = readl_relaxed(PLL_CFG_ALT_REG(regs));
+
+ if (config.mask) {
+ regval &= ~config.mask;
+ regval |= config.val;
+ }
+
+ writel_relaxed(regval, PLL_CFG_ALT_REG(regs));
+}
+
+void __configure_pll(struct pll_config *config,
+ struct pll_config_regs *regs, u32 ena_fsm_mode)
+{
+ u32 regval;
+
+ writel_relaxed(config->l, PLL_L_REG(regs));
+ writel_relaxed(config->m, PLL_M_REG(regs));
+ writel_relaxed(config->n, PLL_N_REG(regs));
+
+ regval = readl_relaxed(PLL_CONFIG_REG(regs));
+
+ /* Enable the MN accumulator */
+ if (config->mn_ena_mask) {
+ regval &= ~config->mn_ena_mask;
+ regval |= config->mn_ena_val;
+ }
+
+ /* Enable the main output */
+ if (config->main_output_mask) {
+ regval &= ~config->main_output_mask;
+ regval |= config->main_output_val;
+ }
+
+ /* Enable the aux output */
+ if (config->aux_output_mask) {
+ regval &= ~config->aux_output_mask;
+ regval |= config->aux_output_val;
+ }
+
+ /* Set pre-divider and post-divider values */
+ regval &= ~config->pre_div_mask;
+ regval |= config->pre_div_val;
+ regval &= ~config->post_div_mask;
+ regval |= config->post_div_val;
+
+ /* Select VCO setting */
+ regval &= ~config->vco_mask;
+ regval |= config->vco_val;
+
+ if (config->add_factor_mask) {
+ regval &= ~config->add_factor_mask;
+ regval |= config->add_factor_val;
+ }
+
+ writel_relaxed(regval, PLL_CONFIG_REG(regs));
+
+ if (regs->config_alt_reg)
+ __configure_alt_config(config->alt_cfg, regs);
+
+ if (regs->config_ctl_reg)
+ writel_relaxed(config->cfg_ctl_val, PLL_CFG_CTL_REG(regs));
+}
+
+void configure_sr_pll(struct pll_config *config,
+ struct pll_config_regs *regs, u32 ena_fsm_mode)
+{
+ __configure_pll(config, regs, ena_fsm_mode);
+ if (ena_fsm_mode)
+ __set_fsm_mode(PLL_MODE_REG(regs), 0x1, 0x8);
+}
+
+void configure_sr_hpm_lp_pll(struct pll_config *config,
+ struct pll_config_regs *regs, u32 ena_fsm_mode)
+{
+ __configure_pll(config, regs, ena_fsm_mode);
+ if (ena_fsm_mode)
+ __set_fsm_mode(PLL_MODE_REG(regs), 0x1, 0x0);
+}
+
+static void *votable_pll_clk_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct pll_vote_clk *v, *peer;
+ struct clk *c;
+ u32 val, rc;
+ phandle p;
+ struct msmclk_data *drv;
+
+ v = devm_kzalloc(dev, sizeof(*v), GFP_KERNEL);
+ if (!v) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ drv = msmclk_parse_phandle(dev, np->parent->phandle);
+ if (IS_ERR_OR_NULL(drv))
+ return ERR_CAST(drv);
+ v->base = &drv->base;
+
+ rc = of_property_read_u32(np, "qcom,en-offset", (u32 *)&v->en_reg);
+ if (rc) {
+ dt_err(np, "missing qcom,en-offset dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,en-bit", &val);
+ if (rc) {
+ dt_err(np, "missing qcom,en-bit dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+ v->en_mask = BIT(val);
+
+ rc = of_property_read_u32(np, "qcom,status-offset",
+ (u32 *)&v->status_reg);
+ if (rc) {
+ dt_err(np, "missing qcom,status-offset dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ rc = of_property_read_u32(np, "qcom,status-bit", &val);
+ if (rc) {
+ dt_err(np, "missing qcom,status-bit dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+ v->status_mask = BIT(val);
+
+ rc = of_property_read_u32(np, "qcom,pll-config-rate", &val);
+ if (rc) {
+ dt_err(np, "missing qcom,pll-config-rate dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+ v->c.rate = val;
+
+ if (of_device_is_compatible(np, "qcom,active-only-pll"))
+ v->soft_vote_mask = PLL_SOFT_VOTE_ACPU;
+ else if (of_device_is_compatible(np, "qcom,sleep-active-pll"))
+ v->soft_vote_mask = PLL_SOFT_VOTE_PRIMARY;
+
+ if (of_device_is_compatible(np, "qcom,votable-pll")) {
+ v->c.ops = &clk_ops_pll_vote;
+ return msmclk_generic_clk_init(dev, np, &v->c);
+ }
+
+ rc = of_property_read_phandle_index(np, "qcom,peer", 0, &p);
+ if (rc) {
+ dt_err(np, "missing qcom,peer dt property\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ c = msmclk_lookup_phandle(dev, p);
+ if (!IS_ERR_OR_NULL(c)) {
+ v->soft_vote = devm_kzalloc(dev, sizeof(*v->soft_vote),
+ GFP_KERNEL);
+ if (!v->soft_vote) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ peer = to_pll_vote_clk(c);
+ peer->soft_vote = v->soft_vote;
+ }
+
+ v->c.ops = &clk_ops_pll_acpu_vote;
+ return msmclk_generic_clk_init(dev, np, &v->c);
+}
+MSMCLK_PARSER(votable_pll_clk_dt_parser, "qcom,active-only-pll", 0);
+MSMCLK_PARSER(votable_pll_clk_dt_parser, "qcom,sleep-active-pll", 1);
+MSMCLK_PARSER(votable_pll_clk_dt_parser, "qcom,votable-pll", 2);
diff --git a/drivers/clk/msm/clock-rpm.c b/drivers/clk/msm/clock-rpm.c
new file mode 100644
index 000000000000..ac093463ff23
--- /dev/null
+++ b/drivers/clk/msm/clock-rpm.c
@@ -0,0 +1,472 @@
+/* Copyright (c) 2010-2015, 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/err.h>
+#include <linux/rtmutex.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <soc/qcom/clock-rpm.h>
+#include <soc/qcom/msm-clock-controller.h>
+
+#define __clk_rpmrs_set_rate(r, value, ctx) \
+ ((r)->rpmrs_data->set_rate_fn((r), (value), (ctx)))
+
+#define clk_rpmrs_set_rate_sleep(r, value) \
+ __clk_rpmrs_set_rate((r), (value), (r)->rpmrs_data->ctx_sleep_id)
+
+#define clk_rpmrs_set_rate_active(r, value) \
+ __clk_rpmrs_set_rate((r), (value), (r)->rpmrs_data->ctx_active_id)
+
+static int clk_rpmrs_set_rate_smd(struct rpm_clk *r, uint32_t value,
+ uint32_t context)
+{
+ int ret;
+
+ struct msm_rpm_kvp kvp = {
+ .key = r->rpm_key,
+ .data = (void *)&value,
+ .length = sizeof(value),
+ };
+
+ switch (context) {
+ case MSM_RPM_CTX_ACTIVE_SET:
+ if (*r->last_active_set_vote == value)
+ return 0;
+ break;
+ case MSM_RPM_CTX_SLEEP_SET:
+ if (*r->last_sleep_set_vote == value)
+ return 0;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ ret = msm_rpm_send_message(context, r->rpm_res_type, r->rpm_clk_id,
+ &kvp, 1);
+ if (ret)
+ return ret;
+
+ switch (context) {
+ case MSM_RPM_CTX_ACTIVE_SET:
+ *r->last_active_set_vote = value;
+ break;
+ case MSM_RPM_CTX_SLEEP_SET:
+ *r->last_sleep_set_vote = value;
+ break;
+ }
+
+ return 0;
+}
+
+static int clk_rpmrs_handoff_smd(struct rpm_clk *r)
+{
+ if (!r->branch)
+ r->c.rate = INT_MAX;
+
+ return 0;
+}
+
+static int clk_rpmrs_is_enabled_smd(struct rpm_clk *r)
+{
+ return !!r->c.prepare_count;
+}
+
+struct clk_rpmrs_data {
+ int (*set_rate_fn)(struct rpm_clk *r, uint32_t value, uint32_t context);
+ int (*get_rate_fn)(struct rpm_clk *r);
+ int (*handoff_fn)(struct rpm_clk *r);
+ int (*is_enabled)(struct rpm_clk *r);
+ int ctx_active_id;
+ int ctx_sleep_id;
+};
+
+struct clk_rpmrs_data clk_rpmrs_data_smd = {
+ .set_rate_fn = clk_rpmrs_set_rate_smd,
+ .handoff_fn = clk_rpmrs_handoff_smd,
+ .is_enabled = clk_rpmrs_is_enabled_smd,
+ .ctx_active_id = MSM_RPM_CTX_ACTIVE_SET,
+ .ctx_sleep_id = MSM_RPM_CTX_SLEEP_SET,
+};
+
+static DEFINE_RT_MUTEX(rpm_clock_lock);
+
+static void to_active_sleep_khz(struct rpm_clk *r, unsigned long rate,
+ unsigned long *active_khz, unsigned long *sleep_khz)
+{
+ /* Convert the rate (hz) to khz */
+ *active_khz = DIV_ROUND_UP(rate, 1000);
+
+ /*
+ * Active-only clocks don't care what the rate is during sleep. So,
+ * they vote for zero.
+ */
+ if (r->active_only)
+ *sleep_khz = 0;
+ else
+ *sleep_khz = *active_khz;
+}
+
+static int rpm_clk_prepare(struct clk *clk)
+{
+ struct rpm_clk *r = to_rpm_clk(clk);
+ uint32_t value;
+ int rc = 0;
+ unsigned long this_khz, this_sleep_khz;
+ unsigned long peer_khz = 0, peer_sleep_khz = 0;
+ struct rpm_clk *peer = r->peer;
+
+ rt_mutex_lock(&rpm_clock_lock);
+
+ to_active_sleep_khz(r, r->c.rate, &this_khz, &this_sleep_khz);
+
+ /* Don't send requests to the RPM if the rate has not been set. */
+ if (this_khz == 0)
+ goto out;
+
+ /* Take peer clock's rate into account only if it's enabled. */
+ if (peer->enabled)
+ to_active_sleep_khz(peer, peer->c.rate,
+ &peer_khz, &peer_sleep_khz);
+
+ value = max(this_khz, peer_khz);
+ if (r->branch)
+ value = !!value;
+
+ rc = clk_rpmrs_set_rate_active(r, value);
+ if (rc)
+ goto out;
+
+ value = max(this_sleep_khz, peer_sleep_khz);
+ if (r->branch)
+ value = !!value;
+
+ rc = clk_rpmrs_set_rate_sleep(r, value);
+ if (rc) {
+ /* Undo the active set vote and restore it to peer_khz */
+ value = peer_khz;
+ rc = clk_rpmrs_set_rate_active(r, value);
+ }
+
+out:
+ if (!rc)
+ r->enabled = true;
+
+ rt_mutex_unlock(&rpm_clock_lock);
+
+ return rc;
+}
+
+static void rpm_clk_unprepare(struct clk *clk)
+{
+ struct rpm_clk *r = to_rpm_clk(clk);
+
+ rt_mutex_lock(&rpm_clock_lock);
+
+ if (r->c.rate) {
+ uint32_t value;
+ struct rpm_clk *peer = r->peer;
+ unsigned long peer_khz = 0, peer_sleep_khz = 0;
+ int rc;
+
+ /* Take peer clock's rate into account only if it's enabled. */
+ if (peer->enabled)
+ to_active_sleep_khz(peer, peer->c.rate,
+ &peer_khz, &peer_sleep_khz);
+
+ value = r->branch ? !!peer_khz : peer_khz;
+ rc = clk_rpmrs_set_rate_active(r, value);
+ if (rc)
+ goto out;
+
+ value = r->branch ? !!peer_sleep_khz : peer_sleep_khz;
+ rc = clk_rpmrs_set_rate_sleep(r, value);
+ }
+ r->enabled = false;
+out:
+ rt_mutex_unlock(&rpm_clock_lock);
+
+ return;
+}
+
+static int rpm_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ struct rpm_clk *r = to_rpm_clk(clk);
+ unsigned long this_khz, this_sleep_khz;
+ int rc = 0;
+
+ rt_mutex_lock(&rpm_clock_lock);
+
+ if (r->enabled) {
+ uint32_t value;
+ struct rpm_clk *peer = r->peer;
+ unsigned long peer_khz = 0, peer_sleep_khz = 0;
+
+ to_active_sleep_khz(r, rate, &this_khz, &this_sleep_khz);
+
+ /* Take peer clock's rate into account only if it's enabled. */
+ if (peer->enabled)
+ to_active_sleep_khz(peer, peer->c.rate,
+ &peer_khz, &peer_sleep_khz);
+
+ value = max(this_khz, peer_khz);
+ rc = clk_rpmrs_set_rate_active(r, value);
+ if (rc)
+ goto out;
+
+ value = max(this_sleep_khz, peer_sleep_khz);
+ rc = clk_rpmrs_set_rate_sleep(r, value);
+ }
+
+out:
+ rt_mutex_unlock(&rpm_clock_lock);
+
+ return rc;
+}
+
+static unsigned long rpm_clk_get_rate(struct clk *clk)
+{
+ struct rpm_clk *r = to_rpm_clk(clk);
+ if (r->rpmrs_data->get_rate_fn)
+ return r->rpmrs_data->get_rate_fn(r);
+ else
+ return clk->rate;
+}
+
+static int rpm_clk_is_enabled(struct clk *clk)
+{
+ struct rpm_clk *r = to_rpm_clk(clk);
+ return r->rpmrs_data->is_enabled(r);
+}
+
+static long rpm_clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ /* Not supported. */
+ return rate;
+}
+
+static bool rpm_clk_is_local(struct clk *clk)
+{
+ return false;
+}
+
+static enum handoff rpm_clk_handoff(struct clk *clk)
+{
+ struct rpm_clk *r = to_rpm_clk(clk);
+ int rc;
+
+ /*
+ * Querying an RPM clock's status will return 0 unless the clock's
+ * rate has previously been set through the RPM. When handing off,
+ * assume these clocks are enabled (unless the RPM call fails) so
+ * child clocks of these RPM clocks can still be handed off.
+ */
+ rc = r->rpmrs_data->handoff_fn(r);
+ if (rc < 0)
+ return HANDOFF_DISABLED_CLK;
+
+ /*
+ * Since RPM handoff code may update the software rate of the clock by
+ * querying the RPM, we need to make sure our request to RPM now
+ * matches the software rate of the clock. When we send the request
+ * to RPM, we also need to update any other state info we would
+ * normally update. So, call the appropriate clock function instead
+ * of directly using the RPM driver APIs.
+ */
+ rc = rpm_clk_prepare(clk);
+ if (rc < 0)
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+#define RPM_MISC_CLK_TYPE 0x306b6c63
+#define RPM_SCALING_ENABLE_ID 0x2
+
+int enable_rpm_scaling(void)
+{
+ int rc, value = 0x1;
+ static int is_inited;
+
+ struct msm_rpm_kvp kvp = {
+ .key = RPM_SMD_KEY_ENABLE,
+ .data = (void *)&value,
+ .length = sizeof(value),
+ };
+
+ if (is_inited)
+ return 0;
+
+ rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
+ RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ WARN(1, "RPM clock scaling (sleep set) did not enable!\n");
+ return rc;
+ }
+
+ rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
+ RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ WARN(1, "RPM clock scaling (active set) did not enable!\n");
+ return rc;
+ }
+
+ is_inited++;
+ return 0;
+}
+
+int vote_bimc(struct rpm_clk *r, uint32_t value)
+{
+ int rc;
+
+ struct msm_rpm_kvp kvp = {
+ .key = r->rpm_key,
+ .data = (void *)&value,
+ .length = sizeof(value),
+ };
+
+ rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
+ r->rpm_res_type, r->rpmrs_data->ctx_active_id,
+ &kvp, 1);
+ if (rc < 0) {
+ if (rc != -EPROBE_DEFER)
+ WARN(1, "BIMC vote not sent!\n");
+ return rc;
+ }
+
+ return rc;
+}
+
+struct clk_ops clk_ops_rpm = {
+ .prepare = rpm_clk_prepare,
+ .unprepare = rpm_clk_unprepare,
+ .set_rate = rpm_clk_set_rate,
+ .get_rate = rpm_clk_get_rate,
+ .is_enabled = rpm_clk_is_enabled,
+ .round_rate = rpm_clk_round_rate,
+ .is_local = rpm_clk_is_local,
+ .handoff = rpm_clk_handoff,
+};
+
+struct clk_ops clk_ops_rpm_branch = {
+ .prepare = rpm_clk_prepare,
+ .unprepare = rpm_clk_unprepare,
+ .is_local = rpm_clk_is_local,
+ .handoff = rpm_clk_handoff,
+};
+
+static struct rpm_clk *rpm_clk_dt_parser_common(struct device *dev,
+ struct device_node *np)
+{
+ struct rpm_clk *rpm, *peer;
+ struct clk *c;
+ int rc = 0;
+ phandle p;
+ const char *str;
+
+ rpm = devm_kzalloc(dev, sizeof(*rpm), GFP_KERNEL);
+ if (!rpm) {
+ dt_err(np, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rc = of_property_read_phandle_index(np, "qcom,rpm-peer", 0, &p);
+ if (rc) {
+ dt_err(np, "missing qcom,rpm-peer dt property\n");
+ return ERR_PTR(rc);
+ }
+
+ /* Rely on whoever's called last to setup the circular ref */
+ c = msmclk_lookup_phandle(dev, p);
+ if (!IS_ERR(c)) {
+ uint32_t *sleep = devm_kzalloc(dev, sizeof(uint32_t),
+ GFP_KERNEL);
+ uint32_t *active =
+ devm_kzalloc(dev, sizeof(uint32_t),
+ GFP_KERNEL);
+
+ if (!sleep || !active)
+ return ERR_PTR(-ENOMEM);
+ peer = to_rpm_clk(c);
+ peer->peer = rpm;
+ rpm->peer = peer;
+ rpm->last_active_set_vote = active;
+ peer->last_active_set_vote = active;
+ rpm->last_sleep_set_vote = sleep;
+ peer->last_sleep_set_vote = sleep;
+ }
+
+ rpm->rpmrs_data = &clk_rpmrs_data_smd;
+ rpm->active_only = of_device_is_compatible(np, "qcom,rpm-a-clk") ||
+ of_device_is_compatible(np, "qcom,rpm-branch-a-clk");
+
+ rc = of_property_read_string(np, "qcom,res-type", &str);
+ if (rc) {
+ dt_err(np, "missing qcom,res-type dt property\n");
+ return ERR_PTR(rc);
+ }
+ if (sscanf(str, "%4c", (char *) &rpm->rpm_res_type) <= 0)
+ return ERR_PTR(-EINVAL);
+
+ rc = of_property_read_u32(np, "qcom,res-id", &rpm->rpm_clk_id);
+ if (rc) {
+ dt_err(np, "missing qcom,res-id dt property\n");
+ return ERR_PTR(rc);
+ }
+
+ rc = of_property_read_string(np, "qcom,key", &str);
+ if (rc) {
+ dt_err(np, "missing qcom,key dt property\n");
+ return ERR_PTR(rc);
+ }
+ if (sscanf(str, "%4c", (char *) &rpm->rpm_key) <= 0)
+ return ERR_PTR(-EINVAL);
+ return rpm;
+}
+
+static void *rpm_clk_dt_parser(struct device *dev, struct device_node *np)
+{
+ struct rpm_clk *rpm;
+ rpm = rpm_clk_dt_parser_common(dev, np);
+ if (IS_ERR(rpm))
+ return rpm;
+
+ rpm->c.ops = &clk_ops_rpm;
+ return msmclk_generic_clk_init(dev, np, &rpm->c);
+}
+
+static void *rpm_branch_clk_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct rpm_clk *rpm;
+ u32 rate;
+ int rc;
+ rpm = rpm_clk_dt_parser_common(dev, np);
+ if (IS_ERR(rpm))
+ return rpm;
+
+ rpm->c.ops = &clk_ops_rpm_branch;
+ rpm->branch = true;
+
+ rc = of_property_read_u32(np, "qcom,rcg-init-rate", &rate);
+ if (!rc)
+ rpm->c.rate = rate;
+
+ return msmclk_generic_clk_init(dev, np, &rpm->c);
+}
+MSMCLK_PARSER(rpm_clk_dt_parser, "qcom,rpm-clk", 0);
+MSMCLK_PARSER(rpm_clk_dt_parser, "qcom,rpm-a-clk", 1);
+MSMCLK_PARSER(rpm_branch_clk_dt_parser, "qcom,rpm-branch-clk", 0);
+MSMCLK_PARSER(rpm_branch_clk_dt_parser, "qcom,rpm-branch-a-clk", 1);
diff --git a/drivers/clk/msm/clock-voter.c b/drivers/clk/msm/clock-voter.c
new file mode 100644
index 000000000000..0d609743ffc7
--- /dev/null
+++ b/drivers/clk/msm/clock-voter.c
@@ -0,0 +1,202 @@
+/* Copyright (c) 2010-2015, 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/err.h>
+#include <linux/rtmutex.h>
+#include <linux/clk.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <soc/qcom/clock-voter.h>
+#include <soc/qcom/msm-clock-controller.h>
+
+static DEFINE_RT_MUTEX(voter_clk_lock);
+
+/* Aggregate the rate of clocks that are currently on. */
+static unsigned long voter_clk_aggregate_rate(const struct clk *parent)
+{
+ struct clk *clk;
+ unsigned long rate = 0;
+
+ list_for_each_entry(clk, &parent->children, siblings) {
+ struct clk_voter *v = to_clk_voter(clk);
+ if (v->enabled)
+ rate = max(clk->rate, rate);
+ }
+ return rate;
+}
+
+static int voter_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret = 0;
+ struct clk *clkp;
+ struct clk_voter *clkh, *v = to_clk_voter(clk);
+ unsigned long cur_rate, new_rate, other_rate = 0;
+
+ if (v->is_branch)
+ return 0;
+
+ rt_mutex_lock(&voter_clk_lock);
+
+ if (v->enabled) {
+ struct clk *parent = clk->parent;
+
+ /*
+ * Get the aggregate rate without this clock's vote and update
+ * if the new rate is different than the current rate
+ */
+ list_for_each_entry(clkp, &parent->children, siblings) {
+ clkh = to_clk_voter(clkp);
+ if (clkh->enabled && clkh != v)
+ other_rate = max(clkp->rate, other_rate);
+ }
+
+ cur_rate = max(other_rate, clk->rate);
+ new_rate = max(other_rate, rate);
+
+ if (new_rate != cur_rate) {
+ ret = clk_set_rate(parent, new_rate);
+ if (ret)
+ goto unlock;
+ }
+ }
+ clk->rate = rate;
+unlock:
+ rt_mutex_unlock(&voter_clk_lock);
+
+ return ret;
+}
+
+static int voter_clk_prepare(struct clk *clk)
+{
+ int ret = 0;
+ unsigned long cur_rate;
+ struct clk *parent;
+ struct clk_voter *v = to_clk_voter(clk);
+
+ rt_mutex_lock(&voter_clk_lock);
+ parent = clk->parent;
+
+ if (v->is_branch) {
+ v->enabled = true;
+ goto out;
+ }
+
+ /*
+ * Increase the rate if this clock is voting for a higher rate
+ * than the current rate.
+ */
+ cur_rate = voter_clk_aggregate_rate(parent);
+ if (clk->rate > cur_rate) {
+ ret = clk_set_rate(parent, clk->rate);
+ if (ret)
+ goto out;
+ }
+ v->enabled = true;
+out:
+ rt_mutex_unlock(&voter_clk_lock);
+
+ return ret;
+}
+
+static void voter_clk_unprepare(struct clk *clk)
+{
+ unsigned long cur_rate, new_rate;
+ struct clk *parent;
+ struct clk_voter *v = to_clk_voter(clk);
+
+
+ rt_mutex_lock(&voter_clk_lock);
+ parent = clk->parent;
+
+ /*
+ * Decrease the rate if this clock was the only one voting for
+ * the highest rate.
+ */
+ v->enabled = false;
+ if (v->is_branch)
+ goto out;
+
+ new_rate = voter_clk_aggregate_rate(parent);
+ cur_rate = max(new_rate, clk->rate);
+
+ if (new_rate < cur_rate)
+ clk_set_rate(parent, new_rate);
+
+out:
+ rt_mutex_unlock(&voter_clk_lock);
+}
+
+static int voter_clk_is_enabled(struct clk *clk)
+{
+ struct clk_voter *v = to_clk_voter(clk);
+ return v->enabled;
+}
+
+static long voter_clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ return clk_round_rate(clk->parent, rate);
+}
+
+static bool voter_clk_is_local(struct clk *clk)
+{
+ return true;
+}
+
+static enum handoff voter_clk_handoff(struct clk *clk)
+{
+ if (!clk->rate)
+ return HANDOFF_DISABLED_CLK;
+
+ /*
+ * Send the default rate to the parent if necessary and update the
+ * software state of the voter clock.
+ */
+ if (voter_clk_prepare(clk) < 0)
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
+struct clk_ops clk_ops_voter = {
+ .prepare = voter_clk_prepare,
+ .unprepare = voter_clk_unprepare,
+ .set_rate = voter_clk_set_rate,
+ .is_enabled = voter_clk_is_enabled,
+ .round_rate = voter_clk_round_rate,
+ .is_local = voter_clk_is_local,
+ .handoff = voter_clk_handoff,
+};
+
+static void *sw_vote_clk_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct clk_voter *v;
+ int rc;
+ u32 temp;
+
+ v = devm_kzalloc(dev, sizeof(*v), GFP_KERNEL);
+ if (!v) {
+ dt_err(np, "failed to alloc memory\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rc = of_property_read_u32(np, "qcom,config-rate", &temp);
+ if (rc) {
+ dt_prop_err(np, "qcom,config-rate", "is missing");
+ return ERR_PTR(rc);
+ }
+
+ v->c.ops = &clk_ops_voter;
+ return msmclk_generic_clk_init(dev, np, &v->c);
+}
+MSMCLK_PARSER(sw_vote_clk_dt_parser, "qcom,sw-vote-clk", 0);
diff --git a/drivers/clk/msm/clock.c b/drivers/clk/msm/clock.c
new file mode 100644
index 000000000000..8b30a1bc7eb5
--- /dev/null
+++ b/drivers/clk/msm/clock.c
@@ -0,0 +1,1389 @@
+/* arch/arm/mach-msm/clock.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/err.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/list.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/of_platform.h>
+#include <linux/pm_opp.h>
+
+#include <trace/events/power.h>
+#include "clock.h"
+
+struct handoff_clk {
+ struct list_head list;
+ struct clk *clk;
+};
+static LIST_HEAD(handoff_list);
+
+struct handoff_vdd {
+ struct list_head list;
+ struct clk_vdd_class *vdd_class;
+};
+static LIST_HEAD(handoff_vdd_list);
+
+static DEFINE_MUTEX(msm_clock_init_lock);
+LIST_HEAD(orphan_clk_list);
+static LIST_HEAD(clk_notifier_list);
+
+/* Find the voltage level required for a given rate. */
+int find_vdd_level(struct clk *clk, unsigned long rate)
+{
+ int level;
+
+ for (level = 0; level < clk->num_fmax; level++)
+ if (rate <= clk->fmax[level])
+ break;
+
+ if (level == clk->num_fmax) {
+ pr_err("Rate %lu for %s is greater than highest Fmax\n", rate,
+ clk->dbg_name);
+ return -EINVAL;
+ }
+
+ return level;
+}
+
+/* Update voltage level given the current votes. */
+static int update_vdd(struct clk_vdd_class *vdd_class)
+{
+ int level, rc = 0, i, ignore;
+ struct regulator **r = vdd_class->regulator;
+ int *uv = vdd_class->vdd_uv;
+ int *ua = vdd_class->vdd_ua;
+ int n_reg = vdd_class->num_regulators;
+ int cur_lvl = vdd_class->cur_level;
+ int max_lvl = vdd_class->num_levels - 1;
+ int cur_base = cur_lvl * n_reg;
+ int new_base;
+
+ /* aggregate votes */
+ for (level = max_lvl; level > 0; level--)
+ if (vdd_class->level_votes[level])
+ break;
+
+ if (level == cur_lvl)
+ return 0;
+
+ max_lvl = max_lvl * n_reg;
+ new_base = level * n_reg;
+ for (i = 0; i < vdd_class->num_regulators; i++) {
+ rc = regulator_set_voltage(r[i], uv[new_base + i],
+ vdd_class->use_max_uV ? INT_MAX : uv[max_lvl + i]);
+ if (rc)
+ goto set_voltage_fail;
+
+ if (ua) {
+ rc = regulator_set_load(r[i], ua[new_base + i]);
+ rc = rc > 0 ? 0 : rc;
+ if (rc)
+ goto set_mode_fail;
+ }
+ if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels)
+ rc = regulator_enable(r[i]);
+ else if (level == 0)
+ rc = regulator_disable(r[i]);
+ if (rc)
+ goto enable_disable_fail;
+ }
+ if (vdd_class->set_vdd && !vdd_class->num_regulators)
+ rc = vdd_class->set_vdd(vdd_class, level);
+
+ if (!rc)
+ vdd_class->cur_level = level;
+
+ return rc;
+
+enable_disable_fail:
+ /*
+ * set_optimum_mode could use voltage to derive mode. Restore
+ * previous voltage setting for r[i] first.
+ */
+ if (ua) {
+ regulator_set_voltage(r[i], uv[cur_base + i],
+ vdd_class->use_max_uV ? INT_MAX : uv[max_lvl + i]);
+ regulator_set_load(r[i], ua[cur_base + i]);
+ }
+
+set_mode_fail:
+ regulator_set_voltage(r[i], uv[cur_base + i],
+ vdd_class->use_max_uV ? INT_MAX : uv[max_lvl + i]);
+
+set_voltage_fail:
+ for (i--; i >= 0; i--) {
+ regulator_set_voltage(r[i], uv[cur_base + i],
+ vdd_class->use_max_uV ? INT_MAX : uv[max_lvl + i]);
+ if (ua)
+ regulator_set_load(r[i], ua[cur_base + i]);
+ if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels)
+ regulator_disable(r[i]);
+ else if (level == 0)
+ ignore = regulator_enable(r[i]);
+ }
+ return rc;
+}
+
+/* Vote for a voltage level. */
+int vote_vdd_level(struct clk_vdd_class *vdd_class, int level)
+{
+ int rc;
+
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
+ mutex_lock(&vdd_class->lock);
+ vdd_class->level_votes[level]++;
+ rc = update_vdd(vdd_class);
+ if (rc)
+ vdd_class->level_votes[level]--;
+ mutex_unlock(&vdd_class->lock);
+
+ return rc;
+}
+
+/* Remove vote for a voltage level. */
+int unvote_vdd_level(struct clk_vdd_class *vdd_class, int level)
+{
+ int rc = 0;
+
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
+ mutex_lock(&vdd_class->lock);
+ if (WARN(!vdd_class->level_votes[level],
+ "Reference counts are incorrect for %s level %d\n",
+ vdd_class->class_name, level))
+ goto out;
+ vdd_class->level_votes[level]--;
+ rc = update_vdd(vdd_class);
+ if (rc)
+ vdd_class->level_votes[level]++;
+out:
+ mutex_unlock(&vdd_class->lock);
+ return rc;
+}
+
+/* Vote for a voltage level corresponding to a clock's rate. */
+static int vote_rate_vdd(struct clk *clk, unsigned long rate)
+{
+ int level;
+
+ if (!clk->vdd_class)
+ return 0;
+
+ level = find_vdd_level(clk, rate);
+ if (level < 0)
+ return level;
+
+ return vote_vdd_level(clk->vdd_class, level);
+}
+
+/* Remove vote for a voltage level corresponding to a clock's rate. */
+static void unvote_rate_vdd(struct clk *clk, unsigned long rate)
+{
+ int level;
+
+ if (!clk->vdd_class)
+ return;
+
+ level = find_vdd_level(clk, rate);
+ if (level < 0)
+ return;
+
+ unvote_vdd_level(clk->vdd_class, level);
+}
+
+/* Check if the rate is within the voltage limits of the clock. */
+bool is_rate_valid(struct clk *clk, unsigned long rate)
+{
+ int level;
+
+ if (!clk->vdd_class)
+ return true;
+
+ level = find_vdd_level(clk, rate);
+ return level >= 0;
+}
+
+/**
+ * __clk_pre_reparent() - Set up the new parent before switching to it and
+ * prevent the enable state of the child clock from changing.
+ * @c: The child clock that's going to switch parents
+ * @new: The new parent that the child clock is going to switch to
+ * @flags: Pointer to scratch space to save spinlock flags
+ *
+ * Cannot be called from atomic context.
+ *
+ * Use this API to set up the @new parent clock to be able to support the
+ * current prepare and enable state of the child clock @c. Once the parent is
+ * set up, the child clock can safely switch to it.
+ *
+ * The caller shall grab the prepare_lock of clock @c before calling this API
+ * and only release it after calling __clk_post_reparent() for clock @c (or
+ * if this API fails). This is necessary to prevent the prepare state of the
+ * child clock @c from changing while the reparenting is in progress. Since
+ * this API takes care of grabbing the enable lock of @c, only atomic
+ * operation are allowed between calls to __clk_pre_reparent and
+ * __clk_post_reparent()
+ *
+ * The scratch space pointed to by @flags should not be altered before
+ * calling __clk_post_reparent() for clock @c.
+ *
+ * See also: __clk_post_reparent()
+ */
+int __clk_pre_reparent(struct clk *c, struct clk *new, unsigned long *flags)
+{
+ int rc;
+
+ if (c->prepare_count) {
+ rc = clk_prepare(new);
+ if (rc)
+ return rc;
+ }
+
+ spin_lock_irqsave(&c->lock, *flags);
+ if (c->count) {
+ rc = clk_enable(new);
+ if (rc) {
+ spin_unlock_irqrestore(&c->lock, *flags);
+ clk_unprepare(new);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+/**
+ * __clk_post_reparent() - Release requirements on old parent after switching
+ * away from it and allow changes to the child clock's enable state.
+ * @c: The child clock that switched parents
+ * @old: The old parent that the child clock switched away from or the new
+ * parent of a failed reparent attempt.
+ * @flags: Pointer to scratch space where spinlock flags were saved
+ *
+ * Cannot be called from atomic context.
+ *
+ * This API works in tandem with __clk_pre_reparent. Use this API to
+ * - Remove prepare and enable requirements from the @old parent after
+ * switching away from it
+ * - Or, undo the effects of __clk_pre_reparent() after a failed attempt to
+ * change parents
+ *
+ * The caller shall release the prepare_lock of @c that was grabbed before
+ * calling __clk_pre_reparent() only after this API is called (or if
+ * __clk_pre_reparent() fails). This is necessary to prevent the prepare
+ * state of the child clock @c from changing while the reparenting is in
+ * progress. Since this API releases the enable lock of @c, the limit to
+ * atomic operations set by __clk_pre_reparent() is no longer present.
+ *
+ * The scratch space pointed to by @flags shall not be altered since the call
+ * to __clk_pre_reparent() for clock @c.
+ *
+ * See also: __clk_pre_reparent()
+ */
+void __clk_post_reparent(struct clk *c, struct clk *old, unsigned long *flags)
+{
+ if (c->count)
+ clk_disable(old);
+ spin_unlock_irqrestore(&c->lock, *flags);
+
+ if (c->prepare_count)
+ clk_unprepare(old);
+}
+
+int clk_prepare(struct clk *clk)
+{
+ int ret = 0;
+ struct clk *parent;
+
+ if (!clk)
+ return 0;
+ if (IS_ERR(clk))
+ return -EINVAL;
+
+ mutex_lock(&clk->prepare_lock);
+ if (clk->prepare_count == 0) {
+ parent = clk->parent;
+
+ ret = clk_prepare(parent);
+ if (ret)
+ goto out;
+ ret = clk_prepare(clk->depends);
+ if (ret)
+ goto err_prepare_depends;
+
+ ret = vote_rate_vdd(clk, clk->rate);
+ if (ret)
+ goto err_vote_vdd;
+ if (clk->ops->prepare)
+ ret = clk->ops->prepare(clk);
+ if (ret)
+ goto err_prepare_clock;
+ }
+ clk->prepare_count++;
+out:
+ mutex_unlock(&clk->prepare_lock);
+ return ret;
+err_prepare_clock:
+ unvote_rate_vdd(clk, clk->rate);
+err_vote_vdd:
+ clk_unprepare(clk->depends);
+err_prepare_depends:
+ clk_unprepare(parent);
+ goto out;
+}
+EXPORT_SYMBOL(clk_prepare);
+
+/*
+ * Standard clock functions defined in include/linux/clk.h
+ */
+int clk_enable(struct clk *clk)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct clk *parent;
+ const char *name;
+
+ if (!clk)
+ return 0;
+ if (IS_ERR(clk))
+ return -EINVAL;
+ name = clk->dbg_name;
+
+ spin_lock_irqsave(&clk->lock, flags);
+ WARN(!clk->prepare_count,
+ "%s: Don't call enable on unprepared clocks\n", name);
+ if (clk->count == 0) {
+ parent = clk->parent;
+
+ ret = clk_enable(parent);
+ if (ret)
+ goto err_enable_parent;
+ ret = clk_enable(clk->depends);
+ if (ret)
+ goto err_enable_depends;
+
+ trace_clock_enable(name, 1, smp_processor_id());
+ if (clk->ops->enable)
+ ret = clk->ops->enable(clk);
+ if (ret)
+ goto err_enable_clock;
+ }
+ clk->count++;
+ spin_unlock_irqrestore(&clk->lock, flags);
+
+ return 0;
+
+err_enable_clock:
+ clk_disable(clk->depends);
+err_enable_depends:
+ clk_disable(parent);
+err_enable_parent:
+ spin_unlock_irqrestore(&clk->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+ const char *name;
+ unsigned long flags;
+
+ if (IS_ERR_OR_NULL(clk))
+ return;
+ name = clk->dbg_name;
+
+ spin_lock_irqsave(&clk->lock, flags);
+ WARN(!clk->prepare_count,
+ "%s: Never called prepare or calling disable after unprepare\n",
+ name);
+ if (WARN(clk->count == 0, "%s is unbalanced", name))
+ goto out;
+ if (clk->count == 1) {
+ struct clk *parent = clk->parent;
+
+ trace_clock_disable(name, 0, smp_processor_id());
+ if (clk->ops->disable)
+ clk->ops->disable(clk);
+ clk_disable(clk->depends);
+ clk_disable(parent);
+ }
+ clk->count--;
+out:
+ spin_unlock_irqrestore(&clk->lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+void clk_unprepare(struct clk *clk)
+{
+ const char *name;
+
+ if (IS_ERR_OR_NULL(clk))
+ return;
+ name = clk->dbg_name;
+
+ mutex_lock(&clk->prepare_lock);
+ if (WARN(!clk->prepare_count, "%s is unbalanced (prepare)", name))
+ goto out;
+ if (clk->prepare_count == 1) {
+ struct clk *parent = clk->parent;
+
+ WARN(clk->count,
+ "%s: Don't call unprepare when the clock is enabled\n",
+ name);
+
+ if (clk->ops->unprepare)
+ clk->ops->unprepare(clk);
+ unvote_rate_vdd(clk, clk->rate);
+ clk_unprepare(clk->depends);
+ clk_unprepare(parent);
+ }
+ clk->prepare_count--;
+out:
+ mutex_unlock(&clk->prepare_lock);
+}
+EXPORT_SYMBOL(clk_unprepare);
+
+int clk_reset(struct clk *clk, enum clk_reset_action action)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+
+ if (!clk->ops->reset)
+ return -ENOSYS;
+
+ return clk->ops->reset(clk, action);
+}
+EXPORT_SYMBOL(clk_reset);
+
+/**
+ * __clk_notify - call clk notifier chain
+ * @clk: struct clk * that is changing rate
+ * @msg: clk notifier type (see include/linux/clk.h)
+ * @old_rate: old clk rate
+ * @new_rate: new clk rate
+ *
+ * Triggers a notifier call chain on the clk rate-change notification
+ * for 'clk'. Passes a pointer to the struct clk and the previous
+ * and current rates to the notifier callback. Intended to be called by
+ * internal clock code only. Returns NOTIFY_DONE from the last driver
+ * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if
+ * a driver returns that.
+ */
+static int __clk_notify(struct clk *clk, unsigned long msg,
+ unsigned long old_rate, unsigned long new_rate)
+{
+ struct msm_clk_notifier *cn;
+ struct msm_clk_notifier_data cnd;
+ int ret = NOTIFY_DONE;
+
+ cnd.clk = clk;
+ cnd.old_rate = old_rate;
+ cnd.new_rate = new_rate;
+
+ list_for_each_entry(cn, &clk_notifier_list, node) {
+ if (cn->clk == clk) {
+ ret = srcu_notifier_call_chain(&cn->notifier_head, msg,
+ &cnd);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * clk rate change notifiers
+ *
+ * Note - The following notifier functionality is a verbatim copy
+ * of the implementation in the common clock framework, copied here
+ * until MSM switches to the common clock framework.
+ */
+
+/**
+ * msm_clk_notif_register - add a clk rate change notifier
+ * @clk: struct clk * to watch
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request notification when clk's rate changes. This uses an SRCU
+ * notifier because we want it to block and notifier unregistrations are
+ * uncommon. The callbacks associated with the notifier must not
+ * re-enter into the clk framework by calling any top-level clk APIs;
+ * this will cause a nested prepare_lock mutex.
+ *
+ * Pre-change notifier callbacks will be passed the current, pre-change
+ * rate of the clk via struct msm_clk_notifier_data.old_rate. The new,
+ * post-change rate of the clk is passed via struct
+ * msm_clk_notifier_data.new_rate.
+ *
+ * Post-change notifiers will pass the now-current, post-change rate of
+ * the clk in both struct msm_clk_notifier_data.old_rate and struct
+ * msm_clk_notifier_data.new_rate.
+ *
+ * Abort-change notifiers are effectively the opposite of pre-change
+ * notifiers: the original pre-change clk rate is passed in via struct
+ * msm_clk_notifier_data.new_rate and the failed post-change rate is passed
+ * in via struct msm_clk_notifier_data.old_rate.
+ *
+ * msm_clk_notif_register() must be called from non-atomic context.
+ * Returns -EINVAL if called with null arguments, -ENOMEM upon
+ * allocation failure; otherwise, passes along the return value of
+ * srcu_notifier_chain_register().
+ */
+int msm_clk_notif_register(struct clk *clk, struct notifier_block *nb)
+{
+ struct msm_clk_notifier *cn;
+ int ret = -ENOMEM;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ mutex_lock(&clk->prepare_lock);
+
+ /* search the list of notifiers for this clk */
+ list_for_each_entry(cn, &clk_notifier_list, node)
+ if (cn->clk == clk)
+ break;
+
+ /* if clk wasn't in the notifier list, allocate new clk_notifier */
+ if (cn->clk != clk) {
+ cn = kzalloc(sizeof(struct msm_clk_notifier), GFP_KERNEL);
+ if (!cn)
+ goto out;
+
+ cn->clk = clk;
+ srcu_init_notifier_head(&cn->notifier_head);
+
+ list_add(&cn->node, &clk_notifier_list);
+ }
+
+ ret = srcu_notifier_chain_register(&cn->notifier_head, nb);
+
+ clk->notifier_count++;
+
+out:
+ mutex_unlock(&clk->prepare_lock);
+
+ return ret;
+}
+
+/**
+ * msm_clk_notif_unregister - remove a clk rate change notifier
+ * @clk: struct clk *
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request no further notification for changes to 'clk' and frees memory
+ * allocated in msm_clk_notifier_register.
+ *
+ * Returns -EINVAL if called with null arguments; otherwise, passes
+ * along the return value of srcu_notifier_chain_unregister().
+ */
+int msm_clk_notif_unregister(struct clk *clk, struct notifier_block *nb)
+{
+ struct msm_clk_notifier *cn = NULL;
+ int ret = -EINVAL;
+
+ if (!clk || !nb)
+ return -EINVAL;
+
+ mutex_lock(&clk->prepare_lock);
+
+ list_for_each_entry(cn, &clk_notifier_list, node)
+ if (cn->clk == clk)
+ break;
+
+ if (cn->clk == clk) {
+ ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb);
+
+ clk->notifier_count--;
+
+ /* XXX the notifier code should handle this better */
+ if (!cn->notifier_head.head) {
+ srcu_cleanup_notifier_head(&cn->notifier_head);
+ list_del(&cn->node);
+ kfree(cn);
+ }
+
+ } else {
+ ret = -ENOENT;
+ }
+
+ mutex_unlock(&clk->prepare_lock);
+
+ return ret;
+}
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return 0;
+
+ if (!clk->ops->get_rate)
+ return clk->rate;
+
+ return clk->ops->get_rate(clk);
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long start_rate;
+ int rc = 0;
+ const char *name;
+
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+ name = clk->dbg_name;
+
+ if (!is_rate_valid(clk, rate))
+ return -EINVAL;
+
+ mutex_lock(&clk->prepare_lock);
+
+ /* Return early if the rate isn't going to change */
+ if (clk->rate == rate && !(clk->flags & CLKFLAG_NO_RATE_CACHE))
+ goto out;
+
+ if (!clk->ops->set_rate) {
+ rc = -ENOSYS;
+ goto out;
+ }
+
+ trace_clock_set_rate(name, rate, raw_smp_processor_id());
+
+ start_rate = clk->rate;
+
+ if (clk->notifier_count)
+ __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, rate);
+
+ if (clk->ops->pre_set_rate) {
+ rc = clk->ops->pre_set_rate(clk, rate);
+ if (rc)
+ goto abort_set_rate;
+ }
+
+ /* Enforce vdd requirements for target frequency. */
+ if (clk->prepare_count) {
+ rc = vote_rate_vdd(clk, rate);
+ if (rc)
+ goto err_vote_vdd;
+ }
+
+ rc = clk->ops->set_rate(clk, rate);
+ if (rc)
+ goto err_set_rate;
+ clk->rate = rate;
+
+ /* Release vdd requirements for starting frequency. */
+ if (clk->prepare_count)
+ unvote_rate_vdd(clk, start_rate);
+
+ if (clk->ops->post_set_rate)
+ clk->ops->post_set_rate(clk, start_rate);
+
+ if (clk->notifier_count)
+ __clk_notify(clk, POST_RATE_CHANGE, start_rate, clk->rate);
+
+ trace_clock_set_rate_complete(name, clk->rate, raw_smp_processor_id());
+out:
+ mutex_unlock(&clk->prepare_lock);
+ return rc;
+
+abort_set_rate:
+ __clk_notify(clk, ABORT_RATE_CHANGE, clk->rate, rate);
+err_set_rate:
+ if (clk->prepare_count)
+ unvote_rate_vdd(clk, rate);
+err_vote_vdd:
+ /* clk->rate is still the old rate. So, pass the new rate instead. */
+ if (clk->ops->post_set_rate)
+ clk->ops->post_set_rate(clk, rate);
+ goto out;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ long rrate;
+ unsigned long fmax = 0, i;
+
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+
+ for (i = 0; i < clk->num_fmax; i++)
+ fmax = max(fmax, clk->fmax[i]);
+ if (!fmax)
+ fmax = ULONG_MAX;
+ rate = min(rate, fmax);
+
+ if (clk->ops->round_rate)
+ rrate = clk->ops->round_rate(clk, rate);
+ else if (clk->rate)
+ rrate = clk->rate;
+ else
+ return -ENOSYS;
+
+ if (rrate > fmax)
+ return -EINVAL;
+ return rrate;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_max_rate(struct clk *clk, unsigned long rate)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+
+ if (!clk->ops->set_max_rate)
+ return -ENOSYS;
+
+ return clk->ops->set_max_rate(clk, rate);
+}
+EXPORT_SYMBOL(clk_set_max_rate);
+
+int parent_to_src_sel(struct clk_src *parents, int num_parents, struct clk *p)
+{
+ int i;
+
+ for (i = 0; i < num_parents; i++) {
+ if (parents[i].src == p)
+ return parents[i].sel;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(parent_to_src_sel);
+
+int clk_get_parent_sel(struct clk *c, struct clk *parent)
+{
+ return parent_to_src_sel(c->parents, c->num_parents, parent);
+}
+EXPORT_SYMBOL(clk_get_parent_sel);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int rc = 0;
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+
+ if (!clk->ops->set_parent && clk->parent == parent)
+ return 0;
+
+ if (!clk->ops->set_parent)
+ return -ENOSYS;
+
+ mutex_lock(&clk->prepare_lock);
+ if (clk->parent == parent && !(clk->flags & CLKFLAG_NO_RATE_CACHE))
+ goto out;
+ rc = clk->ops->set_parent(clk, parent);
+out:
+ mutex_unlock(&clk->prepare_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return NULL;
+
+ return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+int clk_set_flags(struct clk *clk, unsigned long flags)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+ if (!clk->ops->set_flags)
+ return -ENOSYS;
+
+ return clk->ops->set_flags(clk, flags);
+}
+EXPORT_SYMBOL(clk_set_flags);
+
+static LIST_HEAD(initdata_list);
+
+static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks)
+{
+ struct clk *clk, *parent;
+ unsigned n;
+
+ for (n = 0; n < num_clocks; n++) {
+ clk = clock_tbl[n].clk;
+ parent = clk->parent;
+ if (parent && list_empty(&clk->siblings))
+ list_add(&clk->siblings, &parent->children);
+ }
+}
+
+static void vdd_class_init(struct clk_vdd_class *vdd)
+{
+ struct handoff_vdd *v;
+
+ if (!vdd)
+ return;
+
+ if (vdd->skip_handoff)
+ return;
+
+ list_for_each_entry(v, &handoff_vdd_list, list) {
+ if (v->vdd_class == vdd)
+ return;
+ }
+
+ pr_debug("voting for vdd_class %s\n", vdd->class_name);
+ if (vote_vdd_level(vdd, vdd->num_levels - 1))
+ pr_err("failed to vote for %s\n", vdd->class_name);
+
+ v = kmalloc(sizeof(*v), GFP_KERNEL);
+ if (!v) {
+ pr_err("Unable to kmalloc. %s will be stuck at max.\n",
+ vdd->class_name);
+ return;
+ }
+
+ v->vdd_class = vdd;
+ list_add_tail(&v->list, &handoff_vdd_list);
+}
+
+static int __handoff_clk(struct clk *clk)
+{
+ enum handoff state = HANDOFF_DISABLED_CLK;
+ struct handoff_clk *h = NULL;
+ int rc, i;
+
+ if (clk == NULL || clk->flags & CLKFLAG_INIT_DONE ||
+ clk->flags & CLKFLAG_SKIP_HANDOFF)
+ return 0;
+
+ if (clk->flags & CLKFLAG_INIT_ERR)
+ return -ENXIO;
+
+ if (clk->flags & CLKFLAG_EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ /* Handoff any 'depends' clock first. */
+ rc = __handoff_clk(clk->depends);
+ if (rc)
+ goto err;
+
+ /*
+ * Handoff functions for the parent must be called before the
+ * children can be handed off. Without handing off the parents and
+ * knowing their rate and state (on/off), it's impossible to figure
+ * out the rate and state of the children.
+ */
+ if (clk->ops->get_parent)
+ clk->parent = clk->ops->get_parent(clk);
+
+ if (IS_ERR(clk->parent)) {
+ rc = PTR_ERR(clk->parent);
+ goto err;
+ }
+
+ rc = __handoff_clk(clk->parent);
+ if (rc)
+ goto err;
+
+ for (i = 0; i < clk->num_parents; i++) {
+ rc = __handoff_clk(clk->parents[i].src);
+ if (rc)
+ goto err;
+ }
+
+ if (clk->ops->handoff)
+ state = clk->ops->handoff(clk);
+
+ if (state == HANDOFF_ENABLED_CLK) {
+
+ h = kmalloc(sizeof(*h), GFP_KERNEL);
+ if (!h) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = clk_prepare_enable(clk->parent);
+ if (rc)
+ goto err;
+
+ rc = clk_prepare_enable(clk->depends);
+ if (rc)
+ goto err_depends;
+
+ rc = vote_rate_vdd(clk, clk->rate);
+ WARN(rc, "%s unable to vote for voltage!\n", clk->dbg_name);
+
+ clk->count = 1;
+ clk->prepare_count = 1;
+ h->clk = clk;
+ list_add_tail(&h->list, &handoff_list);
+
+ pr_debug("Handed off %s rate=%lu\n", clk->dbg_name, clk->rate);
+ }
+
+ if (clk->init_rate && clk_set_rate(clk, clk->init_rate))
+ pr_err("failed to set an init rate of %lu on %s\n",
+ clk->init_rate, clk->dbg_name);
+ if (clk->always_on && clk_prepare_enable(clk))
+ pr_err("failed to enable always-on clock %s\n",
+ clk->dbg_name);
+
+ clk->flags |= CLKFLAG_INIT_DONE;
+ /* if the clk is on orphan list, remove it */
+ list_del_init(&clk->list);
+ clock_debug_register(clk);
+
+ return 0;
+
+err_depends:
+ clk_disable_unprepare(clk->parent);
+err:
+ kfree(h);
+ if (rc == -EPROBE_DEFER) {
+ clk->flags |= CLKFLAG_EPROBE_DEFER;
+ if (list_empty(&clk->list))
+ list_add_tail(&clk->list, &orphan_clk_list);
+ } else {
+ pr_err("%s handoff failed (%d)\n", clk->dbg_name, rc);
+ clk->flags |= CLKFLAG_INIT_ERR;
+ }
+ return rc;
+}
+
+/**
+ * msm_clock_register() - Register additional clock tables
+ * @table: Table of clocks
+ * @size: Size of @table
+ *
+ * Upon return, clock APIs may be used to control clocks registered using this
+ * function.
+ */
+int msm_clock_register(struct clk_lookup *table, size_t size)
+{
+ int n = 0, rc;
+ struct clk *c, *safe;
+ bool found_more_clks;
+
+ mutex_lock(&msm_clock_init_lock);
+
+ init_sibling_lists(table, size);
+
+ /*
+ * Enable regulators and temporarily set them up at maximum voltage.
+ * Once all the clocks have made their respective vote, remove this
+ * temporary vote. The removing of the temporary vote is done at
+ * late_init, by which time we assume all the clocks would have been
+ * handed off.
+ */
+ for (n = 0; n < size; n++)
+ vdd_class_init(table[n].clk->vdd_class);
+
+ /*
+ * Detect and preserve initial clock state until clock_late_init() or
+ * a driver explicitly changes it, whichever is first.
+ */
+
+ for (n = 0; n < size; n++)
+ __handoff_clk(table[n].clk);
+
+ /* maintain backwards compatibility */
+ if (table[0].con_id || table[0].dev_id)
+ clkdev_add_table(table, size);
+
+ do {
+ found_more_clks = false;
+ /* clear cached __handoff_clk return values */
+ list_for_each_entry_safe(c, safe, &orphan_clk_list, list)
+ c->flags &= ~CLKFLAG_EPROBE_DEFER;
+
+ list_for_each_entry_safe(c, safe, &orphan_clk_list, list) {
+ rc = __handoff_clk(c);
+ if (!rc)
+ found_more_clks = true;
+ }
+ } while (found_more_clks);
+
+ mutex_unlock(&msm_clock_init_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_clock_register);
+
+struct of_msm_provider_data {
+ struct clk_lookup *table;
+ size_t size;
+};
+
+static struct clk *of_clk_src_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct of_msm_provider_data *ofdata = data;
+ int n;
+
+ for (n = 0; n < ofdata->size; n++) {
+ if (clkspec->args[0] == ofdata->table[n].of_idx)
+ return ofdata->table[n].clk;
+ }
+ return ERR_PTR(-ENOENT);
+}
+
+#define MAX_LEN_OPP_HANDLE 50
+#define LEN_OPP_HANDLE 16
+#define LEN_OPP_VCORNER_HANDLE 22
+
+static struct device **derive_device_list(struct clk *clk,
+ struct device_node *np,
+ char *clk_handle_name, int len)
+{
+ int j, count, cpu;
+ struct platform_device *pdev;
+ struct device_node *dev_node;
+ struct device **device_list;
+
+ count = len/sizeof(u32);
+ device_list = kmalloc_array(count, sizeof(struct device *),
+ GFP_KERNEL);
+ if (!device_list)
+ return ERR_PTR(-ENOMEM);
+
+ for (j = 0; j < count; j++) {
+ device_list[j] = NULL;
+ dev_node = of_parse_phandle(np, clk_handle_name, j);
+ if (!dev_node) {
+ pr_err("Unable to get device_node pointer for %s opp-handle (%s)\n",
+ clk->dbg_name, clk_handle_name);
+ goto err_parse_phandle;
+ }
+
+ for_each_possible_cpu(cpu) {
+ if (of_get_cpu_node(cpu, NULL) == dev_node) {
+ device_list[j] = get_cpu_device(cpu);
+ }
+ }
+
+ if (device_list[j])
+ continue;
+
+ pdev = of_find_device_by_node(dev_node);
+ if (!pdev) {
+ pr_err("Unable to find platform_device node for %s opp-handle\n",
+ clk->dbg_name);
+ goto err_parse_phandle;
+ }
+ device_list[j] = &pdev->dev;
+ }
+ return device_list;
+err_parse_phandle:
+ kfree(device_list);
+ return ERR_PTR(-EINVAL);
+}
+
+static int get_voltage(struct clk *clk, unsigned long rate,
+ int store_vcorner, int n)
+{
+ struct clk_vdd_class *vdd;
+ int uv, level, corner;
+
+ /*
+ * Use the first regulator in the vdd class
+ * for the OPP table.
+ */
+ vdd = clk->vdd_class;
+ if (vdd->num_regulators > 1) {
+ corner = vdd->vdd_uv[vdd->num_regulators * n];
+ } else {
+ level = find_vdd_level(clk, rate);
+ if (level < 0) {
+ pr_err("Could not find vdd level\n");
+ return -EINVAL;
+ }
+ corner = vdd->vdd_uv[level];
+ }
+
+ if (!corner) {
+ pr_err("%s: Unable to find vdd level for rate %lu\n",
+ clk->dbg_name, rate);
+ return -EINVAL;
+ }
+
+ if (store_vcorner) {
+ uv = corner;
+ return uv;
+ }
+
+ uv = regulator_list_corner_voltage(vdd->regulator[0], corner);
+ if (uv < 0) {
+ pr_err("%s: no uv for corner %d - err: %d\n",
+ clk->dbg_name, corner, uv);
+ return uv;
+ }
+ return uv;
+}
+
+static int add_and_print_opp(struct clk *clk, struct device **device_list,
+ int count, unsigned long rate, int uv, int n)
+{
+ int j, ret = 0;
+
+ for (j = 0; j < count; j++) {
+ ret = dev_pm_opp_add(device_list[j], rate, uv);
+ if (ret) {
+ pr_err("%s: couldn't add OPP for %lu - err: %d\n",
+ clk->dbg_name, rate, ret);
+ return ret;
+ }
+ if (n == 1 || n == clk->num_fmax - 1 ||
+ rate == clk_round_rate(clk, INT_MAX))
+ pr_info("%s: set OPP pair(%lu Hz: %u uV) on %s\n",
+ clk->dbg_name, rate, uv,
+ dev_name(device_list[j]));
+ }
+ return ret;
+}
+
+static void populate_clock_opp_table(struct device_node *np,
+ struct clk_lookup *table, size_t size)
+{
+ struct device **device_list;
+ struct clk *clk;
+ char clk_handle_name[MAX_LEN_OPP_HANDLE];
+ char clk_store_volt_corner[MAX_LEN_OPP_HANDLE];
+ size_t i;
+ int n, len, count, uv = 0;
+ unsigned long rate, ret = 0;
+ bool store_vcorner;
+
+ /* Iterate across all clocks in the clock controller */
+ for (i = 0; i < size; i++) {
+ n = 1;
+ rate = 0;
+
+ store_vcorner = false;
+ clk = table[i].clk;
+ if (!clk || !clk->num_fmax || clk->opp_table_populated)
+ continue;
+
+ if (strlen(clk->dbg_name) + LEN_OPP_HANDLE
+ < MAX_LEN_OPP_HANDLE) {
+ ret = snprintf(clk_handle_name,
+ ARRAY_SIZE(clk_handle_name),
+ "qcom,%s-opp-handle", clk->dbg_name);
+ if (ret < strlen(clk->dbg_name) + LEN_OPP_HANDLE) {
+ pr_err("Failed to hold clk_handle_name\n");
+ continue;
+ }
+ } else {
+ pr_err("clk name (%s) too large to fit in clk_handle_name\n",
+ clk->dbg_name);
+ continue;
+ }
+
+ if (strlen(clk->dbg_name) + LEN_OPP_VCORNER_HANDLE
+ < MAX_LEN_OPP_HANDLE) {
+ ret = snprintf(clk_store_volt_corner,
+ ARRAY_SIZE(clk_store_volt_corner),
+ "qcom,%s-opp-store-vcorner", clk->dbg_name);
+ if (ret < strlen(clk->dbg_name) +
+ LEN_OPP_VCORNER_HANDLE) {
+ pr_err("Failed to hold clk_store_volt_corner\n");
+ continue;
+ }
+ } else {
+ pr_err("clk name (%s) too large to fit in clk_store_volt_corner\n",
+ clk->dbg_name);
+ continue;
+ }
+
+ if (!of_find_property(np, clk_handle_name, &len)) {
+ pr_debug("Unable to find %s\n", clk_handle_name);
+ if (!of_find_property(np, clk_store_volt_corner,
+ &len)) {
+ pr_debug("Unable to find %s\n",
+ clk_store_volt_corner);
+ continue;
+ } else {
+ store_vcorner = true;
+ device_list = derive_device_list(clk, np,
+ clk_store_volt_corner, len);
+ }
+ } else
+ device_list = derive_device_list(clk, np,
+ clk_handle_name, len);
+ if (IS_ERR_OR_NULL(device_list)) {
+ pr_err("Failed to fill device_list\n");
+ continue;
+ }
+
+ count = len/sizeof(u32);
+ while (1) {
+ /*
+ * Calling clk_round_rate will not work for all clocks
+ * (eg. mux_div). Use their fmax values instead to get
+ * list of all available frequencies.
+ */
+ if (clk->ops->list_rate) {
+ ret = clk_round_rate(clk, rate + 1);
+ if (ret < 0) {
+ pr_err("clk_round_rate failed for %s\n",
+ clk->dbg_name);
+ goto err_round_rate;
+ }
+ /*
+ * If clk_round_rate give the same value on
+ * consecutive iterations, exit loop since
+ * we're at the maximum clock frequency.
+ */
+ if (rate == ret)
+ break;
+ rate = ret;
+ } else {
+ if (n < clk->num_fmax)
+ rate = clk->fmax[n];
+ else
+ break;
+ }
+
+ uv = get_voltage(clk, rate, store_vcorner, n);
+ if (uv < 0)
+ goto err_round_rate;
+
+ ret = add_and_print_opp(clk, device_list, count,
+ rate, uv , n);
+ if (ret)
+ goto err_round_rate;
+
+ n++;
+ }
+err_round_rate:
+ /* If OPP table population was successful, set the flag */
+ if (uv >= 0 && ret >= 0)
+ clk->opp_table_populated = true;
+ kfree(device_list);
+ }
+}
+
+/**
+ * of_msm_clock_register() - Register clock tables with clkdev and with the
+ * clock DT framework
+ * @table: Table of clocks
+ * @size: Size of @table
+ * @np: Device pointer corresponding to the clock-provider device
+ *
+ * Upon return, clock APIs may be used to control clocks registered using this
+ * function.
+ */
+int of_msm_clock_register(struct device_node *np, struct clk_lookup *table,
+ size_t size)
+{
+ int ret = 0;
+ struct of_msm_provider_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->table = table;
+ data->size = size;
+
+ ret = of_clk_add_provider(np, of_clk_src_get, data);
+ if (ret) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ populate_clock_opp_table(np, table, size);
+ return msm_clock_register(table, size);
+}
+EXPORT_SYMBOL(of_msm_clock_register);
+
+/**
+ * msm_clock_init() - Register and initialize a clock driver
+ * @data: Driver-specific clock initialization data
+ *
+ * Upon return from this call, clock APIs may be used to control
+ * clocks registered with this API.
+ */
+int __init msm_clock_init(struct clock_init_data *data)
+{
+ if (!data)
+ return -EINVAL;
+
+ if (data->pre_init)
+ data->pre_init();
+
+ mutex_lock(&msm_clock_init_lock);
+ if (data->late_init)
+ list_add(&data->list, &initdata_list);
+ mutex_unlock(&msm_clock_init_lock);
+
+ msm_clock_register(data->table, data->size);
+
+ if (data->post_init)
+ data->post_init();
+
+ return 0;
+}
+
+static int __init clock_late_init(void)
+{
+ struct handoff_clk *h, *h_temp;
+ struct handoff_vdd *v, *v_temp;
+ struct clock_init_data *initdata, *initdata_temp;
+ int ret = 0;
+
+ pr_info("%s: Removing enables held for handed-off clocks\n", __func__);
+
+ mutex_lock(&msm_clock_init_lock);
+
+ list_for_each_entry_safe(initdata, initdata_temp,
+ &initdata_list, list) {
+ ret = initdata->late_init();
+ if (ret)
+ pr_err("%s: %pS failed late_init.\n", __func__,
+ initdata);
+ }
+
+ list_for_each_entry_safe(h, h_temp, &handoff_list, list) {
+ clk_disable_unprepare(h->clk);
+ list_del(&h->list);
+ kfree(h);
+ }
+
+ list_for_each_entry_safe(v, v_temp, &handoff_vdd_list, list) {
+ unvote_vdd_level(v->vdd_class, v->vdd_class->num_levels - 1);
+ list_del(&v->list);
+ kfree(v);
+ }
+
+ mutex_unlock(&msm_clock_init_lock);
+
+ return ret;
+}
+/* clock_late_init should run only after all deferred probing
+ * (excluding DLKM probes) has completed.
+ */
+late_initcall_sync(clock_late_init);
diff --git a/drivers/clk/msm/clock.h b/drivers/clk/msm/clock.h
new file mode 100644
index 000000000000..bee769921ff7
--- /dev/null
+++ b/drivers/clk/msm/clock.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __DRIVERS_CLK_MSM_CLOCK_H
+#define __DRIVERS_CLK_MSM_CLOCK_H
+
+#include <linux/clkdev.h>
+
+/**
+ * struct clock_init_data - SoC specific clock initialization data
+ * @table: table of lookups to add
+ * @size: size of @table
+ * @pre_init: called before initializing the clock driver.
+ * @post_init: called after registering @table. clock APIs can be called inside.
+ * @late_init: called during late init
+ */
+struct clock_init_data {
+ struct list_head list;
+ struct clk_lookup *table;
+ size_t size;
+ void (*pre_init)(void);
+ void (*post_init)(void);
+ int (*late_init)(void);
+};
+
+int msm_clock_init(struct clock_init_data *data);
+int find_vdd_level(struct clk *clk, unsigned long rate);
+extern struct list_head orphan_clk_list;
+
+#ifdef CONFIG_DEBUG_FS
+int clock_debug_register(struct clk *clk);
+void clock_debug_print_enabled(void);
+#else
+static inline int clock_debug_register(struct clk *unused)
+{
+ return 0;
+}
+static inline void clock_debug_print_enabled(void) { return; }
+#endif
+
+#endif
diff --git a/drivers/clk/msm/gdsc.c b/drivers/clk/msm/gdsc.c
new file mode 100644
index 000000000000..7d5180502b94
--- /dev/null
+++ b/drivers/clk/msm/gdsc.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2012-2015, 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/clk/msm-clk.h>
+
+#define PWR_ON_MASK BIT(31)
+#define EN_REST_WAIT_MASK (0xF << 20)
+#define EN_FEW_WAIT_MASK (0xF << 16)
+#define CLK_DIS_WAIT_MASK (0xF << 12)
+#define SW_OVERRIDE_MASK BIT(2)
+#define HW_CONTROL_MASK BIT(1)
+#define SW_COLLAPSE_MASK BIT(0)
+#define GMEM_CLAMP_IO_MASK BIT(0)
+#define BCR_BLK_ARES_BIT BIT(0)
+
+/* Wait 2^n CXO cycles between all states. Here, n=2 (4 cycles). */
+#define EN_REST_WAIT_VAL (0x2 << 20)
+#define EN_FEW_WAIT_VAL (0x8 << 16)
+#define CLK_DIS_WAIT_VAL (0x2 << 12)
+
+#define TIMEOUT_US 100
+#define MAX_GDSCR_READS 100
+#define MAX_VOTABLE_GDSCR_READS 200
+
+struct gdsc {
+ struct regulator_dev *rdev;
+ struct regulator_desc rdesc;
+ void __iomem *gdscr;
+ struct clk **clocks;
+ int clock_count;
+ bool toggle_mem;
+ bool toggle_periph;
+ bool toggle_logic;
+ bool resets_asserted;
+ bool root_en;
+ bool force_root_en;
+ int root_clk_idx;
+ bool no_status_check_on_disable;
+ bool is_gdsc_enabled;
+ bool allow_clear;
+ void __iomem *domain_addr;
+ void __iomem *hw_ctrl_addr;
+ void __iomem *sw_reset_addr;
+};
+
+enum gdscr_status {
+ ENABLED,
+ DISABLED,
+};
+
+static DEFINE_MUTEX(gdsc_seq_lock);
+
+void gdsc_allow_clear_retention(struct regulator *regulator)
+{
+ struct gdsc *sc = regulator_get_drvdata(regulator);
+
+ if (sc)
+ sc->allow_clear = true;
+}
+
+static int poll_gdsc_status(struct gdsc *sc, enum gdscr_status status)
+{
+ void __iomem *gdscr;
+ int count;
+ u32 val;
+
+ if (sc->hw_ctrl_addr) {
+ gdscr = sc->hw_ctrl_addr;
+ count = MAX_VOTABLE_GDSCR_READS;
+ } else {
+ gdscr = sc->gdscr;
+ count = MAX_GDSCR_READS;
+ }
+
+ for (; count > 0; count--) {
+ val = readl_relaxed(gdscr);
+ val &= PWR_ON_MASK;
+ switch (status) {
+ case ENABLED:
+ if (val)
+ return 0;
+ break;
+ case DISABLED:
+ if (!val)
+ return 0;
+ break;
+ }
+ /*
+ * There is no guarantee about the delay needed for the enable
+ * bit in the GDSCR to be set or reset after the GDSC state
+ * changes. Hence, keep on checking for a reasonable number
+ * of times until the bit is set with the least possible delay
+ * between succeessive tries.
+ */
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int gdsc_is_enabled(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+
+ if (!sc->toggle_logic)
+ return !sc->resets_asserted;
+
+ regval = readl_relaxed(sc->gdscr);
+ if (regval & PWR_ON_MASK) {
+ /*
+ * The GDSC might be turned on due to TZ/HYP vote on the
+ * votable GDS registers. Check the SW_COLLAPSE_MASK to
+ * determine if HLOS has voted for it.
+ */
+ if (!(regval & SW_COLLAPSE_MASK))
+ return true;
+ }
+ return false;
+}
+
+static int gdsc_enable(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval, hw_ctrl_regval = 0x0;
+ int i, ret = 0;
+
+ mutex_lock(&gdsc_seq_lock);
+
+ if (sc->root_en || sc->force_root_en)
+ clk_prepare_enable(sc->clocks[sc->root_clk_idx]);
+
+ if (sc->toggle_logic) {
+ if (sc->sw_reset_addr) {
+ regval = readl_relaxed(sc->sw_reset_addr);
+ regval |= BCR_BLK_ARES_BIT;
+ writel_relaxed(regval, sc->sw_reset_addr);
+ /*
+ * BLK_ARES should be kept asserted for 1us before
+ * being de-asserted.
+ */
+ wmb();
+ udelay(1);
+
+ regval &= ~BCR_BLK_ARES_BIT;
+ writel_relaxed(regval, sc->sw_reset_addr);
+
+ /* Make sure de-assert goes through before continuing */
+ wmb();
+ }
+
+ if (sc->domain_addr) {
+ regval = readl_relaxed(sc->domain_addr);
+ regval &= ~GMEM_CLAMP_IO_MASK;
+ writel_relaxed(regval, sc->domain_addr);
+ /*
+ * Make sure CLAMP_IO is de-asserted before continuing.
+ */
+ wmb();
+ }
+
+ regval = readl_relaxed(sc->gdscr);
+ if (regval & HW_CONTROL_MASK) {
+ dev_warn(&rdev->dev, "Invalid enable while %s is under HW control\n",
+ sc->rdesc.name);
+ mutex_unlock(&gdsc_seq_lock);
+ return -EBUSY;
+ }
+
+ regval &= ~SW_COLLAPSE_MASK;
+ writel_relaxed(regval, sc->gdscr);
+
+ /* Wait for 8 XO cycles before polling the status bit. */
+ mb();
+ udelay(1);
+
+ ret = poll_gdsc_status(sc, ENABLED);
+ if (ret) {
+ dev_err(&rdev->dev, "%s enable timed out: 0x%x\n",
+ sc->rdesc.name, regval);
+ udelay(TIMEOUT_US);
+ regval = readl_relaxed(sc->gdscr);
+ if (sc->hw_ctrl_addr) {
+ hw_ctrl_regval =
+ readl_relaxed(sc->hw_ctrl_addr);
+ dev_err(&rdev->dev, "%s final state (%d us after timeout): 0x%x, GDS_HW_CTRL: 0x%x\n",
+ sc->rdesc.name, TIMEOUT_US, regval,
+ hw_ctrl_regval);
+ } else
+ dev_err(&rdev->dev, "%s final state: 0x%x (%d us after timeout)\n",
+ sc->rdesc.name, regval, TIMEOUT_US);
+ mutex_unlock(&gdsc_seq_lock);
+ return ret;
+ }
+ } else {
+ for (i = 0; i < sc->clock_count; i++)
+ if (likely(i != sc->root_clk_idx))
+ clk_reset(sc->clocks[i], CLK_RESET_DEASSERT);
+ sc->resets_asserted = false;
+ }
+
+ for (i = 0; i < sc->clock_count; i++) {
+ if (unlikely(i == sc->root_clk_idx))
+ continue;
+ if (sc->toggle_mem)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_MEM);
+ if (sc->toggle_periph)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_PERIPH);
+ }
+
+ /*
+ * If clocks to this power domain were already on, they will take an
+ * additional 4 clock cycles to re-enable after the rail is enabled.
+ * Delay to account for this. A delay is also needed to ensure clocks
+ * are not enabled within 400ns of enabling power to the memories.
+ */
+ udelay(1);
+
+ /* Delay to account for staggered memory powerup. */
+ udelay(1);
+
+ if (sc->force_root_en)
+ clk_disable_unprepare(sc->clocks[sc->root_clk_idx]);
+ sc->is_gdsc_enabled = true;
+
+ mutex_unlock(&gdsc_seq_lock);
+
+ return ret;
+}
+
+static int gdsc_disable(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+ int i, ret = 0;
+
+ mutex_lock(&gdsc_seq_lock);
+
+ if (sc->force_root_en)
+ clk_prepare_enable(sc->clocks[sc->root_clk_idx]);
+
+ for (i = sc->clock_count-1; i >= 0; i--) {
+ if (unlikely(i == sc->root_clk_idx))
+ continue;
+ if (sc->toggle_mem && sc->allow_clear)
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_MEM);
+ if (sc->toggle_periph && sc->allow_clear)
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH);
+ }
+
+ /* Delay to account for staggered memory powerdown. */
+ udelay(1);
+
+ if (sc->toggle_logic) {
+ regval = readl_relaxed(sc->gdscr);
+ if (regval & HW_CONTROL_MASK) {
+ dev_warn(&rdev->dev, "Invalid disable while %s is under HW control\n",
+ sc->rdesc.name);
+ mutex_unlock(&gdsc_seq_lock);
+ return -EBUSY;
+ }
+
+ regval |= SW_COLLAPSE_MASK;
+ writel_relaxed(regval, sc->gdscr);
+ /* Wait for 8 XO cycles before polling the status bit. */
+ mb();
+ udelay(1);
+
+ if (sc->no_status_check_on_disable) {
+ /*
+ * Add a short delay here to ensure that gdsc_enable
+ * right after it was disabled does not put it in a
+ * wierd state.
+ */
+ udelay(TIMEOUT_US);
+ } else {
+ ret = poll_gdsc_status(sc, DISABLED);
+ if (ret)
+ dev_err(&rdev->dev, "%s disable timed out: 0x%x\n",
+ sc->rdesc.name, regval);
+ }
+
+ if (sc->domain_addr) {
+ regval = readl_relaxed(sc->domain_addr);
+ regval |= GMEM_CLAMP_IO_MASK;
+ writel_relaxed(regval, sc->domain_addr);
+ }
+ } else {
+ for (i = sc->clock_count-1; i >= 0; i--)
+ if (likely(i != sc->root_clk_idx))
+ clk_reset(sc->clocks[i], CLK_RESET_ASSERT);
+ sc->resets_asserted = true;
+ }
+
+ /*
+ * Check if gdsc_enable was called for this GDSC. If not, the root
+ * clock will not have been enabled prior to this.
+ */
+ if ((sc->is_gdsc_enabled && sc->root_en) || sc->force_root_en)
+ clk_disable_unprepare(sc->clocks[sc->root_clk_idx]);
+ sc->is_gdsc_enabled = false;
+
+ mutex_unlock(&gdsc_seq_lock);
+
+ return ret;
+}
+
+static unsigned int gdsc_get_mode(struct regulator_dev *rdev)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+
+ mutex_lock(&gdsc_seq_lock);
+ regval = readl_relaxed(sc->gdscr);
+ mutex_unlock(&gdsc_seq_lock);
+ if (regval & HW_CONTROL_MASK)
+ return REGULATOR_MODE_FAST;
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int gdsc_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct gdsc *sc = rdev_get_drvdata(rdev);
+ uint32_t regval;
+ int ret = 0;
+
+ mutex_lock(&gdsc_seq_lock);
+
+ regval = readl_relaxed(sc->gdscr);
+
+ /*
+ * HW control can only be enable/disabled when SW_COLLAPSE
+ * indicates on.
+ */
+ if (regval & SW_COLLAPSE_MASK) {
+ dev_err(&rdev->dev, "can't enable hw collapse now\n");
+ mutex_unlock(&gdsc_seq_lock);
+ return -EBUSY;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ /* Turn on HW trigger mode */
+ regval |= HW_CONTROL_MASK;
+ writel_relaxed(regval, sc->gdscr);
+ /*
+ * There may be a race with internal HW trigger signal,
+ * that will result in GDSC going through a power down and
+ * up cycle. In case HW trigger signal is controlled by
+ * firmware that also poll same status bits as we do, FW
+ * might read an 'on' status before the GDSC can finish
+ * power cycle. We wait 1us before returning to ensure
+ * FW can't immediately poll the status bit.
+ */
+ mb();
+ udelay(1);
+ break;
+
+ case REGULATOR_MODE_NORMAL:
+ /* Turn off HW trigger mode */
+ regval &= ~HW_CONTROL_MASK;
+ writel_relaxed(regval, sc->gdscr);
+ /*
+ * There may be a race with internal HW trigger signal,
+ * that will result in GDSC going through a power down and
+ * up cycle. If we poll too early, status bit will
+ * indicate 'on' before the GDSC can finish the power cycle.
+ * Account for this case by waiting 1us before polling.
+ */
+ mb();
+ udelay(1);
+
+ ret = poll_gdsc_status(sc, ENABLED);
+ if (ret)
+ dev_err(&rdev->dev, "%s set_mode timed out: 0x%x\n",
+ sc->rdesc.name, regval);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&gdsc_seq_lock);
+
+ return ret;
+}
+
+static struct regulator_ops gdsc_ops = {
+ .is_enabled = gdsc_is_enabled,
+ .enable = gdsc_enable,
+ .disable = gdsc_disable,
+ .set_mode = gdsc_set_mode,
+ .get_mode = gdsc_get_mode,
+};
+
+static int gdsc_probe(struct platform_device *pdev)
+{
+ static atomic_t gdsc_count = ATOMIC_INIT(-1);
+ struct regulator_config reg_config = {};
+ struct regulator_init_data *init_data;
+ struct resource *res;
+ struct gdsc *sc;
+ uint32_t regval, clk_dis_wait_val = CLK_DIS_WAIT_VAL;
+ bool retain_mem, retain_periph, support_hw_trigger;
+ int i, ret;
+
+ sc = devm_kzalloc(&pdev->dev, sizeof(struct gdsc), GFP_KERNEL);
+ if (sc == NULL)
+ return -ENOMEM;
+
+ init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node,
+ &sc->rdesc);
+ if (init_data == NULL)
+ return -ENOMEM;
+
+ if (of_get_property(pdev->dev.of_node, "parent-supply", NULL))
+ init_data->supply_regulator = "parent";
+
+ ret = of_property_read_string(pdev->dev.of_node, "regulator-name",
+ &sc->rdesc.name);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL)
+ return -EINVAL;
+ sc->gdscr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (sc->gdscr == NULL)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "domain_addr");
+ if (res) {
+ sc->domain_addr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (sc->domain_addr == NULL)
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "sw_reset");
+ if (res) {
+ sc->sw_reset_addr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (sc->sw_reset_addr == NULL)
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "hw_ctrl_addr");
+ if (res) {
+ sc->hw_ctrl_addr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (sc->hw_ctrl_addr == NULL)
+ return -ENOMEM;
+ }
+
+ sc->clock_count = of_property_count_strings(pdev->dev.of_node,
+ "clock-names");
+ if (sc->clock_count == -EINVAL) {
+ sc->clock_count = 0;
+ } else if (IS_ERR_VALUE(sc->clock_count)) {
+ dev_err(&pdev->dev, "Failed to get clock names\n");
+ return -EINVAL;
+ }
+
+ sc->clocks = devm_kzalloc(&pdev->dev,
+ sizeof(struct clk *) * sc->clock_count, GFP_KERNEL);
+ if (!sc->clocks)
+ return -ENOMEM;
+
+ sc->root_clk_idx = -1;
+
+ sc->root_en = of_property_read_bool(pdev->dev.of_node,
+ "qcom,enable-root-clk");
+ sc->force_root_en = of_property_read_bool(pdev->dev.of_node,
+ "qcom,force-enable-root-clk");
+ for (i = 0; i < sc->clock_count; i++) {
+ const char *clock_name;
+ of_property_read_string_index(pdev->dev.of_node, "clock-names",
+ i, &clock_name);
+ sc->clocks[i] = devm_clk_get(&pdev->dev, clock_name);
+ if (IS_ERR(sc->clocks[i])) {
+ int rc = PTR_ERR(sc->clocks[i]);
+ if (rc != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get %s\n",
+ clock_name);
+ return rc;
+ }
+
+ if (!strcmp(clock_name, "core_root_clk"))
+ sc->root_clk_idx = i;
+ }
+
+ if ((sc->root_en || sc->force_root_en) && (sc->root_clk_idx == -1)) {
+ dev_err(&pdev->dev, "Failed to get root clock name\n");
+ return -EINVAL;
+ }
+
+ sc->rdesc.id = atomic_inc_return(&gdsc_count);
+ sc->rdesc.ops = &gdsc_ops;
+ sc->rdesc.type = REGULATOR_VOLTAGE;
+ sc->rdesc.owner = THIS_MODULE;
+ platform_set_drvdata(pdev, sc);
+
+ /*
+ * Disable HW trigger: collapse/restore occur based on registers writes.
+ * Disable SW override: Use hardware state-machine for sequencing.
+ */
+ regval = readl_relaxed(sc->gdscr);
+ regval &= ~(HW_CONTROL_MASK | SW_OVERRIDE_MASK);
+
+ if (!of_property_read_u32(pdev->dev.of_node, "qcom,clk-dis-wait-val",
+ &clk_dis_wait_val))
+ clk_dis_wait_val = clk_dis_wait_val << 12;
+
+ /* Configure wait time between states. */
+ regval &= ~(EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK);
+ regval |= EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | clk_dis_wait_val;
+ writel_relaxed(regval, sc->gdscr);
+
+ sc->no_status_check_on_disable =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,no-status-check-on-disable");
+ retain_mem = of_property_read_bool(pdev->dev.of_node,
+ "qcom,retain-mem");
+ sc->toggle_mem = !retain_mem;
+ retain_periph = of_property_read_bool(pdev->dev.of_node,
+ "qcom,retain-periph");
+ sc->toggle_periph = !retain_periph;
+ sc->toggle_logic = !of_property_read_bool(pdev->dev.of_node,
+ "qcom,skip-logic-collapse");
+ support_hw_trigger = of_property_read_bool(pdev->dev.of_node,
+ "qcom,support-hw-trigger");
+ if (support_hw_trigger) {
+ init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE;
+ init_data->constraints.valid_modes_mask |=
+ REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+ }
+
+ if (!sc->toggle_logic) {
+ regval &= ~SW_COLLAPSE_MASK;
+ writel_relaxed(regval, sc->gdscr);
+
+ ret = poll_gdsc_status(sc, ENABLED);
+ if (ret) {
+ dev_err(&pdev->dev, "%s enable timed out: 0x%x\n",
+ sc->rdesc.name, regval);
+ return ret;
+ }
+ }
+
+ sc->allow_clear = of_property_read_bool(pdev->dev.of_node,
+ "qcom,disallow-clear");
+ sc->allow_clear = !sc->allow_clear;
+
+ for (i = 0; i < sc->clock_count; i++) {
+ if (retain_mem || (regval & PWR_ON_MASK) || !sc->allow_clear)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_MEM);
+ else
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_MEM);
+
+ if (retain_periph || (regval & PWR_ON_MASK) || !sc->allow_clear)
+ clk_set_flags(sc->clocks[i], CLKFLAG_RETAIN_PERIPH);
+ else
+ clk_set_flags(sc->clocks[i], CLKFLAG_NORETAIN_PERIPH);
+ }
+
+ reg_config.dev = &pdev->dev;
+ reg_config.init_data = init_data;
+ reg_config.driver_data = sc;
+ reg_config.of_node = pdev->dev.of_node;
+ sc->rdev = regulator_register(&sc->rdesc, &reg_config);
+ if (IS_ERR(sc->rdev)) {
+ dev_err(&pdev->dev, "regulator_register(\"%s\") failed.\n",
+ sc->rdesc.name);
+ return PTR_ERR(sc->rdev);
+ }
+
+ return 0;
+}
+
+static int gdsc_remove(struct platform_device *pdev)
+{
+ struct gdsc *sc = platform_get_drvdata(pdev);
+ regulator_unregister(sc->rdev);
+ return 0;
+}
+
+static struct of_device_id gdsc_match_table[] = {
+ { .compatible = "qcom,gdsc" },
+ {}
+};
+
+static struct platform_driver gdsc_driver = {
+ .probe = gdsc_probe,
+ .remove = gdsc_remove,
+ .driver = {
+ .name = "gdsc",
+ .of_match_table = gdsc_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gdsc_init(void)
+{
+ return platform_driver_register(&gdsc_driver);
+}
+subsys_initcall(gdsc_init);
+
+static void __exit gdsc_exit(void)
+{
+ platform_driver_unregister(&gdsc_driver);
+}
+module_exit(gdsc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM8974 GDSC power rail regulator driver");
diff --git a/drivers/clk/msm/msm-clock-controller.c b/drivers/clk/msm/msm-clock-controller.c
new file mode 100644
index 000000000000..edce15bc3471
--- /dev/null
+++ b/drivers/clk/msm/msm-clock-controller.c
@@ -0,0 +1,755 @@
+/*
+ * Copyright (c) 2014, 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) "msmclock: %s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+#include <linux/hashtable.h>
+
+#include <linux/clk/msm-clk-provider.h>
+#include <soc/qcom/msm-clock-controller.h>
+#include <soc/qcom/clock-rpm.h>
+
+/* Protects list operations */
+static DEFINE_MUTEX(msmclk_lock);
+static LIST_HEAD(msmclk_parser_list);
+static u32 msmclk_debug;
+
+struct hitem {
+ struct hlist_node list;
+ phandle key;
+ void *ptr;
+};
+
+int of_property_count_phandles(struct device_node *np, char *propname)
+{
+ const __be32 *phandle;
+ int size;
+
+ phandle = of_get_property(np, propname, &size);
+ return phandle ? (size / sizeof(*phandle)) : -EINVAL;
+}
+EXPORT_SYMBOL(of_property_count_phandles);
+
+int of_property_read_phandle_index(struct device_node *np, char *propname,
+ int index, phandle *p)
+{
+ const __be32 *phandle;
+ int size;
+
+ phandle = of_get_property(np, propname, &size);
+ if ((!phandle) || (size < sizeof(*phandle) * (index + 1)))
+ return -EINVAL;
+
+ *p = be32_to_cpup(phandle + index);
+ return 0;
+}
+EXPORT_SYMBOL(of_property_read_phandle_index);
+
+static int generic_vdd_parse_regulators(struct device *dev,
+ struct clk_vdd_class *vdd, struct device_node *np)
+{
+ int num_regulators, i, rc;
+ char *name = "qcom,regulators";
+
+ num_regulators = of_property_count_phandles(np, name);
+ if (num_regulators <= 0) {
+ dt_prop_err(np, name, "missing dt property\n");
+ return -EINVAL;
+ }
+
+ vdd->regulator = devm_kzalloc(dev,
+ sizeof(*vdd->regulator) * num_regulators,
+ GFP_KERNEL);
+ if (!vdd->regulator) {
+ dt_err(np, "memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num_regulators; i++) {
+ phandle p;
+ rc = of_property_read_phandle_index(np, name, i, &p);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read phandle\n");
+ return rc;
+ }
+
+ vdd->regulator[i] = msmclk_parse_phandle(dev, p);
+ if (IS_ERR(vdd->regulator[i])) {
+ dt_prop_err(np, name, "hashtable lookup failed\n");
+ return PTR_ERR(vdd->regulator[i]);
+ }
+ }
+
+ vdd->num_regulators = num_regulators;
+ return 0;
+}
+
+static int generic_vdd_parse_levels(struct device *dev,
+ struct clk_vdd_class *vdd, struct device_node *np)
+{
+ int len, rc;
+ char *name = "qcom,uV-levels";
+
+ if (!of_find_property(np, name, &len)) {
+ dt_prop_err(np, name, "missing dt property\n");
+ return -EINVAL;
+ }
+
+ len /= sizeof(u32);
+ if (len % vdd->num_regulators) {
+ dt_err(np, "mismatch beween qcom,uV-levels and qcom,regulators dt properties\n");
+ return -EINVAL;
+ }
+
+ vdd->num_levels = len / vdd->num_regulators;
+ vdd->vdd_uv = devm_kzalloc(dev, len * sizeof(*vdd->vdd_uv),
+ GFP_KERNEL);
+ vdd->level_votes = devm_kzalloc(dev,
+ vdd->num_levels * sizeof(*vdd->level_votes),
+ GFP_KERNEL);
+
+ if (!vdd->vdd_uv || !vdd->level_votes) {
+ dt_err(np, "memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_u32_array(np, name, vdd->vdd_uv,
+ vdd->num_levels * vdd->num_regulators);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read u32 array\n");
+ return -EINVAL;
+ }
+
+ /* Optional Property */
+ name = "qcom,uA-levels";
+ if (!of_find_property(np, name, &len))
+ return 0;
+
+ len /= sizeof(u32);
+ if (len / vdd->num_regulators != vdd->num_levels) {
+ dt_err(np, "size of qcom,uA-levels and qcom,uV-levels must match\n");
+ return -EINVAL;
+ }
+
+ vdd->vdd_ua = devm_kzalloc(dev, len * sizeof(*vdd->vdd_ua),
+ GFP_KERNEL);
+ if (!vdd->vdd_ua) {
+ dt_err(np, "memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ rc = of_property_read_u32_array(np, name, vdd->vdd_ua,
+ vdd->num_levels * vdd->num_regulators);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read u32 array\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void *simple_vdd_class_dt_parser(struct device *dev,
+ struct device_node *np)
+{
+ struct clk_vdd_class *vdd;
+ int rc = 0;
+
+ vdd = devm_kzalloc(dev, sizeof(*vdd), GFP_KERNEL);
+ if (!vdd) {
+ dev_err(dev, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ mutex_init(&vdd->lock);
+ vdd->class_name = np->name;
+
+ rc = generic_vdd_parse_regulators(dev, vdd, np);
+ rc |= generic_vdd_parse_levels(dev, vdd, np);
+ if (rc) {
+ dt_err(np, "unable to read vdd_class\n");
+ return ERR_PTR(rc);
+ }
+
+ return vdd;
+}
+MSMCLK_PARSER(simple_vdd_class_dt_parser, "qcom,simple-vdd-class", 0);
+
+static int generic_clk_parse_parents(struct device *dev, struct clk *c,
+ struct device_node *np)
+{
+ int rc;
+ phandle p;
+ char *name = "qcom,parent";
+
+ /* This property is optional */
+ if (!of_find_property(np, name, NULL))
+ return 0;
+
+ rc = of_property_read_phandle_index(np, name, 0, &p);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read phandle\n");
+ return rc;
+ }
+
+ c->parent = msmclk_parse_phandle(dev, p);
+ if (IS_ERR(c->parent)) {
+ dt_prop_err(np, name, "hashtable lookup failed\n");
+ return PTR_ERR(c->parent);
+ }
+
+ return 0;
+}
+
+static int generic_clk_parse_vdd(struct device *dev, struct clk *c,
+ struct device_node *np)
+{
+ phandle p;
+ int rc;
+ char *name = "qcom,supply-group";
+
+ /* This property is optional */
+ if (!of_find_property(np, name, NULL))
+ return 0;
+
+ rc = of_property_read_phandle_index(np, name, 0, &p);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read phandle\n");
+ return rc;
+ }
+
+ c->vdd_class = msmclk_parse_phandle(dev, p);
+ if (IS_ERR(c->vdd_class)) {
+ dt_prop_err(np, name, "hashtable lookup failed\n");
+ return PTR_ERR(c->vdd_class);
+ }
+
+ return 0;
+}
+
+static int generic_clk_parse_flags(struct device *dev, struct clk *c,
+ struct device_node *np)
+{
+ int rc;
+ char *name = "qcom,clk-flags";
+
+ /* This property is optional */
+ if (!of_find_property(np, name, NULL))
+ return 0;
+
+ rc = of_property_read_u32(np, name, &c->flags);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read u32\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static int generic_clk_parse_fmax(struct device *dev, struct clk *c,
+ struct device_node *np)
+{
+ u32 prop_len, i;
+ int rc;
+ char *name = "qcom,clk-fmax";
+
+ /* This property is optional */
+ if (!of_find_property(np, name, &prop_len))
+ return 0;
+
+ if (!c->vdd_class) {
+ dt_err(np, "both qcom,clk-fmax and qcom,supply-group must be defined\n");
+ return -EINVAL;
+ }
+
+ prop_len /= sizeof(u32);
+ if (prop_len % 2) {
+ dt_prop_err(np, name, "bad length\n");
+ return -EINVAL;
+ }
+
+ /* Value at proplen - 2 is the index of the last entry in fmax array */
+ rc = of_property_read_u32_index(np, name, prop_len - 2, &c->num_fmax);
+ c->num_fmax += 1;
+ if (rc) {
+ dt_prop_err(np, name, "unable to read u32\n");
+ return rc;
+ }
+
+ c->fmax = devm_kzalloc(dev, sizeof(*c->fmax) * c->num_fmax, GFP_KERNEL);
+ if (!c->fmax) {
+ dev_err(dev, "memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < prop_len; i += 2) {
+ u32 level, value;
+ rc = of_property_read_u32_index(np, name, i, &level);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read u32\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32_index(np, name, i + 1, &value);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read u32\n");
+ return rc;
+ }
+
+ if (level >= c->num_fmax) {
+ dt_prop_err(np, name, "must be sorted\n");
+ return -EINVAL;
+ }
+ c->fmax[level] = value;
+ }
+
+ return 0;
+}
+
+static int generic_clk_add_lookup_tbl_entry(struct device *dev, struct clk *c)
+{
+ struct msmclk_data *drv = dev_get_drvdata(dev);
+ struct clk_lookup *cl;
+
+ if (drv->clk_tbl_size >= drv->max_clk_tbl_size) {
+ dev_err(dev, "child node count should be > clock_count?\n");
+ return -EINVAL;
+ }
+
+ cl = drv->clk_tbl + drv->clk_tbl_size;
+ cl->clk = c;
+ drv->clk_tbl_size++;
+ return 0;
+}
+
+static int generic_clk_parse_depends(struct device *dev, struct clk *c,
+ struct device_node *np)
+{
+ phandle p;
+ int rc;
+ char *name = "qcom,depends";
+
+ /* This property is optional */
+ if (!of_find_property(np, name, NULL))
+ return 0;
+
+ rc = of_property_read_phandle_index(np, name, 0, &p);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read phandle\n");
+ return rc;
+ }
+
+ c->depends = msmclk_parse_phandle(dev, p);
+ if (IS_ERR(c->depends)) {
+ dt_prop_err(np, name, "hashtable lookup failed\n");
+ return PTR_ERR(c->depends);
+ }
+
+ return 0;
+}
+
+static int generic_clk_parse_init_config(struct device *dev, struct clk *c,
+ struct device_node *np)
+{
+ int rc;
+ u32 temp;
+ char *name = "qcom,always-on";
+
+ c->always_on = of_property_read_bool(np, name);
+
+ name = "qcom,config-rate";
+ /* This property is optional */
+ if (!of_find_property(np, name, NULL))
+ return 0;
+
+ rc = of_property_read_u32(np, name, &temp);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read u32\n");
+ return rc;
+ }
+ c->init_rate = temp;
+
+ return rc;
+}
+
+void *msmclk_generic_clk_init(struct device *dev, struct device_node *np,
+ struct clk *c)
+{
+ int rc;
+
+ /* CLK_INIT macro */
+ spin_lock_init(&c->lock);
+ mutex_init(&c->prepare_lock);
+ INIT_LIST_HEAD(&c->children);
+ INIT_LIST_HEAD(&c->siblings);
+ INIT_LIST_HEAD(&c->list);
+ c->dbg_name = np->name;
+
+ rc = generic_clk_add_lookup_tbl_entry(dev, c);
+ rc |= generic_clk_parse_flags(dev, c, np);
+ rc |= generic_clk_parse_parents(dev, c, np);
+ rc |= generic_clk_parse_vdd(dev, c, np);
+ rc |= generic_clk_parse_fmax(dev, c, np);
+ rc |= generic_clk_parse_depends(dev, c, np);
+ rc |= generic_clk_parse_init_config(dev, c, np);
+
+ if (rc) {
+ dt_err(np, "unable to read clk\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return c;
+}
+
+static struct msmclk_parser *msmclk_parser_lookup(struct device_node *np)
+{
+ struct msmclk_parser *item;
+ list_for_each_entry(item, &msmclk_parser_list, list) {
+ if (of_device_is_compatible(np, item->compatible))
+ return item;
+ }
+ return NULL;
+}
+void msmclk_parser_register(struct msmclk_parser *item)
+{
+ mutex_lock(&msmclk_lock);
+ list_add(&item->list, &msmclk_parser_list);
+ mutex_unlock(&msmclk_lock);
+}
+
+static int msmclk_htable_add(struct device *dev, void *result, phandle key);
+
+void *msmclk_parse_dt_node(struct device *dev, struct device_node *np)
+{
+ struct msmclk_parser *parser;
+ phandle key;
+ void *result;
+ int rc;
+
+ key = np->phandle;
+ result = msmclk_lookup_phandle(dev, key);
+ if (!result)
+ return ERR_PTR(-EINVAL);
+
+ if (!of_device_is_available(np)) {
+ dt_err(np, "node is disabled\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ parser = msmclk_parser_lookup(np);
+ if (IS_ERR(parser)) {
+ dt_err(np, "no parser found\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* This may return -EPROBE_DEFER */
+ result = parser->parsedt(dev, np);
+ if (IS_ERR(result)) {
+ dt_err(np, "parsedt failed");
+ return result;
+ }
+
+ rc = msmclk_htable_add(dev, result, key);
+ if (rc)
+ return ERR_PTR(rc);
+
+ return result;
+}
+
+void *msmclk_parse_phandle(struct device *dev, phandle key)
+{
+ struct hitem *item;
+ struct device_node *np;
+ struct msmclk_data *drv = dev_get_drvdata(dev);
+
+ /*
+ * the default phandle value is 0. Since hashtable keys must
+ * be unique, reject the default value.
+ */
+ if (!key)
+ return ERR_PTR(-EINVAL);
+
+ hash_for_each_possible(drv->htable, item, list, key) {
+ if (item->key == key)
+ return item->ptr;
+ }
+
+ np = of_find_node_by_phandle(key);
+ if (!np)
+ return ERR_PTR(-EINVAL);
+
+ return msmclk_parse_dt_node(dev, np);
+}
+EXPORT_SYMBOL(msmclk_parse_phandle);
+
+void *msmclk_lookup_phandle(struct device *dev, phandle key)
+{
+ struct hitem *item;
+ struct msmclk_data *drv = dev_get_drvdata(dev);
+
+ hash_for_each_possible(drv->htable, item, list, key) {
+ if (item->key == key)
+ return item->ptr;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL(msmclk_lookup_phandle);
+
+static int msmclk_htable_add(struct device *dev, void *data, phandle key)
+{
+ struct hitem *item;
+ struct msmclk_data *drv = dev_get_drvdata(dev);
+
+ /*
+ * If there are no phandle references to a node, key == 0. However, if
+ * there is a second node like this, both will have key == 0. This
+ * violates the requirement that hashtable keys be unique. Skip it.
+ */
+ if (!key)
+ return 0;
+
+ if (!IS_ERR(msmclk_lookup_phandle(dev, key))) {
+ struct device_node *np = of_find_node_by_phandle(key);
+ dev_err(dev, "attempt to add duplicate entry for %s\n",
+ np ? np->name : "NULL");
+ return -EINVAL;
+ }
+
+ item = devm_kzalloc(dev, sizeof(*item), GFP_KERNEL);
+ if (!item) {
+ dev_err(dev, "memory alloc failure\n");
+ return -ENOMEM;
+ }
+
+ INIT_HLIST_NODE(&item->list);
+ item->key = key;
+ item->ptr = data;
+
+ hash_add(drv->htable, &item->list, key);
+ return 0;
+}
+
+/*
+ * Currently, regulators are the only elements capable of probe deferral.
+ * Check them first to handle probe deferal efficiently.
+*/
+static int get_ext_regulators(struct device *dev)
+{
+ int num_strings, i, rc;
+ struct device_node *np;
+ void *item;
+ char *name = "qcom,regulator-names";
+
+ np = dev->of_node;
+ /* This property is optional */
+ num_strings = of_property_count_strings(np, name);
+ if (num_strings <= 0)
+ return 0;
+
+ for (i = 0; i < num_strings; i++) {
+ const char *str;
+ char buf[50];
+ phandle key;
+
+ rc = of_property_read_string_index(np, name, i, &str);
+ if (rc) {
+ dt_prop_err(np, name, "unable to read string\n");
+ return rc;
+ }
+
+ item = devm_regulator_get(dev, str);
+ if (IS_ERR(item)) {
+ dev_err(dev, "Failed to get regulator: %s\n", str);
+ return PTR_ERR(item);
+ }
+
+ snprintf(buf, ARRAY_SIZE(buf), "%s-supply", str);
+ rc = of_property_read_phandle_index(np, buf, 0, &key);
+ if (rc) {
+ dt_prop_err(np, buf, "unable to read phandle\n");
+ return rc;
+ }
+
+ rc = msmclk_htable_add(dev, item, key);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+static struct clk *msmclk_clk_get(struct of_phandle_args *clkspec, void *data)
+{
+ phandle key;
+ struct clk *c = ERR_PTR(-ENOENT);
+
+ key = clkspec->args[0];
+ c = msmclk_lookup_phandle(data, key);
+
+ if (!IS_ERR(c) && !(c->flags & CLKFLAG_INIT_DONE))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return c;
+}
+
+static void *regulator_dt_parser(struct device *dev, struct device_node *np)
+{
+ dt_err(np, "regulators should be handled in probe()");
+ return ERR_PTR(-EINVAL);
+}
+MSMCLK_PARSER(regulator_dt_parser, "qcom,rpm-smd-regulator", 0);
+
+static void *msmclk_dt_parser(struct device *dev, struct device_node *np)
+{
+ dt_err(np, "calling into other clock controllers isn't allowed");
+ return ERR_PTR(-EINVAL);
+}
+MSMCLK_PARSER(msmclk_dt_parser, "qcom,msm-clock-controller", 0);
+
+static struct msmclk_data *msmclk_drv_init(struct device *dev)
+{
+ struct msmclk_data *drv;
+ size_t size;
+
+ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv) {
+ dev_err(dev, "memory alloc failure\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ dev_set_drvdata(dev, drv);
+
+ drv->dev = dev;
+ INIT_LIST_HEAD(&drv->list);
+
+ /* This overestimates size */
+ drv->max_clk_tbl_size = of_get_child_count(dev->of_node);
+ size = sizeof(*drv->clk_tbl) * drv->max_clk_tbl_size;
+ drv->clk_tbl = devm_kzalloc(dev, size, GFP_KERNEL);
+ if (!drv->clk_tbl) {
+ dev_err(dev, "memory alloc failure clock table size %zu\n",
+ size);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ hash_init(drv->htable);
+ return drv;
+}
+
+static int msmclk_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct device *dev;
+ struct msmclk_data *drv;
+ struct device_node *child;
+ void *result;
+ int rc = 0;
+
+ dev = &pdev->dev;
+ drv = msmclk_drv_init(dev);
+ if (IS_ERR(drv))
+ return PTR_ERR(drv);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cc-base");
+ if (!res) {
+ dt_err(dev->of_node, "missing cc-base\n");
+ return -EINVAL;
+ }
+ drv->base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!drv->base) {
+ dev_err(dev, "ioremap failed for drv->base\n");
+ return -ENOMEM;
+ }
+ rc = msmclk_htable_add(dev, drv, dev->of_node->phandle);
+ if (rc)
+ return rc;
+
+ rc = enable_rpm_scaling();
+ if (rc)
+ return rc;
+
+ rc = get_ext_regulators(dev);
+ if (rc)
+ return rc;
+
+ /*
+ * Returning -EPROBE_DEFER here is inefficient due to
+ * destroying work 'unnecessarily'
+ */
+ for_each_available_child_of_node(dev->of_node, child) {
+ result = msmclk_parse_dt_node(dev, child);
+ if (!IS_ERR(result))
+ continue;
+ if (!msmclk_debug)
+ return PTR_ERR(result);
+ /*
+ * Parse and report all errors instead of immediately
+ * exiting. Return the first error code.
+ */
+ if (!rc)
+ rc = PTR_ERR(result);
+ }
+ if (rc)
+ return rc;
+
+ rc = of_clk_add_provider(dev->of_node, msmclk_clk_get, dev);
+ if (rc) {
+ dev_err(dev, "of_clk_add_provider failed\n");
+ return rc;
+ }
+
+ /*
+ * can't fail after registering clocks, because users may have
+ * gotten clock references. Failing would delete the memory.
+ */
+ WARN_ON(msm_clock_register(drv->clk_tbl, drv->clk_tbl_size));
+ dev_info(dev, "registered clocks\n");
+
+ return 0;
+}
+
+static struct of_device_id msmclk_match_table[] = {
+ {.compatible = "qcom,msm-clock-controller"},
+ {}
+};
+
+static struct platform_driver msmclk_driver = {
+ .probe = msmclk_probe,
+ .driver = {
+ .name = "msm-clock-controller",
+ .of_match_table = msmclk_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static bool initialized;
+int __init msmclk_init(void)
+{
+ int rc;
+ if (initialized)
+ return 0;
+
+ rc = platform_driver_register(&msmclk_driver);
+ if (rc)
+ return rc;
+ initialized = true;
+ return rc;
+}
+arch_initcall(msmclk_init);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index c56988ac63f7..03b9f6fab0ff 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -780,6 +780,13 @@ static inline void clk_writel(u32 val, u32 __iomem *reg)
struct dentry *clk_debugfs_add_file(struct clk_hw *hw, char *name, umode_t mode,
void *data, const struct file_operations *fops);
#endif
+#else
+struct of_device_id;
+
+static inline void __init of_clk_init(const struct of_device_id *matches)
+{
+ return;
+}
#endif /* CONFIG_COMMON_CLK */
#endif /* CLK_PROVIDER_H */
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 0df4a51e1a78..c06bbd5ce952 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -488,7 +488,7 @@ static inline void clk_disable_unprepare(struct clk *clk)
struct device_node;
struct of_phandle_args;
-#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK)
+#if defined(CONFIG_OF)
struct clk *of_clk_get(struct device_node *np, int index);
struct clk *of_clk_get_by_name(struct device_node *np, const char *name);
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec);
diff --git a/include/linux/clk/gdsc.h b/include/linux/clk/gdsc.h
new file mode 100644
index 000000000000..b012ed06d3f4
--- /dev/null
+++ b/include/linux/clk/gdsc.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#ifndef __GDSC_H
+#define __GDSC_H
+
+#include <linux/regulator/consumer.h>
+
+/* Allow the clock memories to be turned off */
+void gdsc_allow_clear_retention(struct regulator *regulator);
+
+#endif
diff --git a/include/linux/clk/msm-clk-provider.h b/include/linux/clk/msm-clk-provider.h
new file mode 100644
index 000000000000..a09ce5c3b156
--- /dev/null
+++ b/include/linux/clk/msm-clk-provider.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MSM_CLK_PROVIDER_H
+#define __MSM_CLK_PROVIDER_H
+
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/seq_file.h>
+#include <linux/clk/msm-clk.h>
+
+/*
+ * Bit manipulation macros
+ */
+#define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb)
+#define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb))
+
+/*
+ * Halt/Status Checking Mode Macros
+ */
+#define HALT 0 /* Bit pol: 1 = halted */
+#define NOCHECK 1 /* No bit to check, do nothing */
+#define HALT_VOTED 2 /* Bit pol: 1 = halted; delay on disable */
+#define ENABLE 3 /* Bit pol: 1 = running */
+#define ENABLE_VOTED 4 /* Bit pol: 1 = running; delay on disable */
+#define DELAY 5 /* No bit to check, just delay */
+
+struct clk_register_data {
+ char *name;
+ u32 offset;
+};
+#ifdef CONFIG_DEBUG_FS
+void clk_debug_print_hw(struct clk *clk, struct seq_file *f);
+#else
+static inline void clk_debug_print_hw(struct clk *clk, struct seq_file *f) {}
+#endif
+
+#define CLK_WARN(clk, cond, fmt, ...) do { \
+ clk_debug_print_hw(clk, NULL); \
+ WARN(cond, "%s: " fmt, clk_name(clk), ##__VA_ARGS__); \
+} while (0)
+
+/**
+ * struct clk_vdd_class - Voltage scaling class
+ * @class_name: name of the class
+ * @regulator: array of regulators.
+ * @num_regulators: size of regulator array. Standard regulator APIs will be
+ used if this field > 0.
+ * @set_vdd: function to call when applying a new voltage setting.
+ * @vdd_uv: sorted 2D array of legal voltage settings. Indexed by level, then
+ regulator.
+ * @vdd_ua: sorted 2D array of legal cureent settings. Indexed by level, then
+ regulator. Optional parameter.
+ * @level_votes: array of votes for each level.
+ * @num_levels: specifies the size of level_votes array.
+ * @skip_handoff: do not vote for the max possible voltage during init
+ * @use_max_uV: use INT_MAX for max_uV when calling regulator_set_voltage
+ * This is useful when different vdd_class share same regulator.
+ * @cur_level: the currently set voltage level
+ * @lock: lock to protect this struct
+ */
+struct clk_vdd_class {
+ const char *class_name;
+ struct regulator **regulator;
+ int num_regulators;
+ int (*set_vdd)(struct clk_vdd_class *v_class, int level);
+ int *vdd_uv;
+ int *vdd_ua;
+ int *level_votes;
+ int num_levels;
+ bool skip_handoff;
+ bool use_max_uV;
+ unsigned long cur_level;
+ struct mutex lock;
+};
+
+#define DEFINE_VDD_CLASS(_name, _set_vdd, _num_levels) \
+ struct clk_vdd_class _name = { \
+ .class_name = #_name, \
+ .set_vdd = _set_vdd, \
+ .level_votes = (int [_num_levels]) {}, \
+ .num_levels = _num_levels, \
+ .cur_level = _num_levels, \
+ .lock = __MUTEX_INITIALIZER(_name.lock) \
+ }
+
+#define DEFINE_VDD_REGULATORS(_name, _num_levels, _num_regulators, _vdd_uv, \
+ _vdd_ua) \
+ struct clk_vdd_class _name = { \
+ .class_name = #_name, \
+ .vdd_uv = _vdd_uv, \
+ .vdd_ua = _vdd_ua, \
+ .regulator = (struct regulator * [_num_regulators]) {}, \
+ .num_regulators = _num_regulators, \
+ .level_votes = (int [_num_levels]) {}, \
+ .num_levels = _num_levels, \
+ .cur_level = _num_levels, \
+ .lock = __MUTEX_INITIALIZER(_name.lock) \
+ }
+
+#define DEFINE_VDD_REGS_INIT(_name, _num_regulators) \
+ struct clk_vdd_class _name = { \
+ .class_name = #_name, \
+ .regulator = (struct regulator * [_num_regulators]) {}, \
+ .num_regulators = _num_regulators, \
+ .lock = __MUTEX_INITIALIZER(_name.lock) \
+ }
+
+enum handoff {
+ HANDOFF_ENABLED_CLK,
+ HANDOFF_DISABLED_CLK,
+};
+
+struct clk_ops {
+ int (*prepare)(struct clk *clk);
+ int (*enable)(struct clk *clk);
+ void (*disable)(struct clk *clk);
+ void (*unprepare)(struct clk *clk);
+ void (*enable_hwcg)(struct clk *clk);
+ void (*disable_hwcg)(struct clk *clk);
+ int (*in_hwcg_mode)(struct clk *clk);
+ enum handoff (*handoff)(struct clk *clk);
+ int (*reset)(struct clk *clk, enum clk_reset_action action);
+ int (*pre_set_rate)(struct clk *clk, unsigned long new_rate);
+ int (*set_rate)(struct clk *clk, unsigned long rate);
+ void (*post_set_rate)(struct clk *clk, unsigned long old_rate);
+ int (*set_max_rate)(struct clk *clk, unsigned long rate);
+ int (*set_flags)(struct clk *clk, unsigned flags);
+ unsigned long (*get_rate)(struct clk *clk);
+ long (*list_rate)(struct clk *clk, unsigned n);
+ int (*is_enabled)(struct clk *clk);
+ long (*round_rate)(struct clk *clk, unsigned long rate);
+ int (*set_parent)(struct clk *clk, struct clk *parent);
+ struct clk *(*get_parent)(struct clk *clk);
+ bool (*is_local)(struct clk *clk);
+ void __iomem *(*list_registers)(struct clk *clk, int n,
+ struct clk_register_data **regs, u32 *size);
+};
+
+/**
+ * struct clk
+ * @prepare_count: prepare refcount
+ * @prepare_lock: protects clk_prepare()/clk_unprepare() path and @prepare_count
+ * @count: enable refcount
+ * @lock: protects clk_enable()/clk_disable() path and @count
+ * @depends: non-direct parent of clock to enable when this clock is enabled
+ * @vdd_class: voltage scaling requirement class
+ * @fmax: maximum frequency in Hz supported at each voltage level
+ * @parent: the current source of this clock
+ * @opp_table_populated: tracks if the OPP table of this clock has been filled
+ */
+struct clk {
+ uint32_t flags;
+ struct clk_ops *ops;
+ const char *dbg_name;
+ struct clk *depends;
+ struct clk_vdd_class *vdd_class;
+ unsigned long *fmax;
+ int num_fmax;
+ unsigned long rate;
+ struct clk *parent;
+ struct clk_src *parents;
+ unsigned int num_parents;
+
+ struct list_head children;
+ struct list_head siblings;
+ struct list_head list;
+
+ unsigned count;
+ unsigned notifier_count;
+ spinlock_t lock;
+ unsigned prepare_count;
+ struct mutex prepare_lock;
+
+ unsigned long init_rate;
+ bool always_on;
+ bool opp_table_populated;
+
+ struct dentry *clk_dir;
+};
+
+#define CLK_INIT(name) \
+ .lock = __SPIN_LOCK_UNLOCKED((name).lock), \
+ .prepare_lock = __MUTEX_INITIALIZER((name).prepare_lock), \
+ .children = LIST_HEAD_INIT((name).children), \
+ .siblings = LIST_HEAD_INIT((name).siblings), \
+ .list = LIST_HEAD_INIT((name).list)
+
+bool is_rate_valid(struct clk *clk, unsigned long rate);
+int vote_vdd_level(struct clk_vdd_class *vdd_class, int level);
+int unvote_vdd_level(struct clk_vdd_class *vdd_class, int level);
+int __clk_pre_reparent(struct clk *c, struct clk *new, unsigned long *flags);
+void __clk_post_reparent(struct clk *c, struct clk *old, unsigned long *flags);
+
+/* Register clocks with the MSM clock driver */
+int msm_clock_register(struct clk_lookup *table, size_t size);
+int of_msm_clock_register(struct device_node *np, struct clk_lookup *table,
+ size_t size);
+
+int clock_rcgwr_init(struct platform_device *pdev);
+int clock_rcgwr_disable(struct platform_device *pdev);
+
+extern struct clk dummy_clk;
+extern struct clk_ops clk_ops_dummy;
+
+#define CLK_DUMMY(clk_name, clk_id, clk_dev, flags) { \
+ .con_id = clk_name, \
+ .dev_id = clk_dev, \
+ .clk = &dummy_clk, \
+ }
+
+#define DEFINE_CLK_DUMMY(name, _rate) \
+ static struct fixed_clk name = { \
+ .c = { \
+ .dbg_name = #name, \
+ .rate = _rate, \
+ .ops = &clk_ops_dummy, \
+ CLK_INIT(name.c), \
+ }, \
+ };
+
+#define CLK_LOOKUP(con, c, dev) { .con_id = con, .clk = &c, .dev_id = dev }
+#define CLK_LOOKUP_OF(con, _c, dev) { .con_id = con, .clk = &(&_c)->c, \
+ .dev_id = dev, .of_idx = clk_##_c }
+#define CLK_LIST(_c) { .clk = &(&_c)->c, .of_idx = clk_##_c }
+
+static inline bool is_better_rate(unsigned long req, unsigned long best,
+ unsigned long new)
+{
+ if (IS_ERR_VALUE(new))
+ return false;
+
+ return (req <= new && new < best) || (best < req && best < new);
+}
+
+extern int of_clk_add_provider(struct device_node *np,
+ struct clk *(*clk_src_get)(struct of_phandle_args *args,
+ void *data),
+ void *data);
+extern void of_clk_del_provider(struct device_node *np);
+
+static inline const char *clk_name(struct clk *c)
+{
+ if (IS_ERR_OR_NULL(c))
+ return "(null)";
+ return c->dbg_name;
+};
+#endif
diff --git a/include/linux/clk/msm-clk.h b/include/linux/clk/msm-clk.h
new file mode 100644
index 000000000000..1f613dfe33c1
--- /dev/null
+++ b/include/linux/clk/msm-clk.h
@@ -0,0 +1,122 @@
+/* Copyright (c) 2009, 2012-2014 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.
+ */
+#ifndef __MACH_CLK_H
+#define __MACH_CLK_H
+
+#include <linux/notifier.h>
+
+#define CLKFLAG_INVERT 0x00000001
+#define CLKFLAG_NOINVERT 0x00000002
+#define CLKFLAG_NONEST 0x00000004
+#define CLKFLAG_NORESET 0x00000008
+#define CLKFLAG_RETAIN_PERIPH 0x00000010
+#define CLKFLAG_NORETAIN_PERIPH 0x00000020
+#define CLKFLAG_RETAIN_MEM 0x00000040
+#define CLKFLAG_NORETAIN_MEM 0x00000080
+#define CLKFLAG_SKIP_HANDOFF 0x00000100
+#define CLKFLAG_MIN 0x00000400
+#define CLKFLAG_MAX 0x00000800
+#define CLKFLAG_INIT_DONE 0x00001000
+#define CLKFLAG_INIT_ERR 0x00002000
+#define CLKFLAG_NO_RATE_CACHE 0x00004000
+#define CLKFLAG_MEASURE 0x00008000
+#define CLKFLAG_EPROBE_DEFER 0x00010000
+
+struct clk_lookup;
+struct clk;
+
+enum clk_reset_action {
+ CLK_RESET_DEASSERT = 0,
+ CLK_RESET_ASSERT = 1
+};
+
+struct clk_src {
+ struct clk *src;
+ int sel;
+};
+
+/* Rate is maximum clock rate in Hz */
+int clk_set_max_rate(struct clk *clk, unsigned long rate);
+
+/* Assert/Deassert reset to a hardware block associated with a clock */
+int clk_reset(struct clk *clk, enum clk_reset_action action);
+
+/* Set clock-specific configuration parameters */
+int clk_set_flags(struct clk *clk, unsigned long flags);
+
+/* returns the mux selection index associated with a particular parent */
+int parent_to_src_sel(struct clk_src *parents, int num_parents, struct clk *p);
+
+/* returns the mux selection index associated with a particular parent */
+int clk_get_parent_sel(struct clk *c, struct clk *parent);
+
+/**
+ * DOC: clk notifier callback types
+ *
+ * PRE_RATE_CHANGE - called immediately before the clk rate is changed,
+ * to indicate that the rate change will proceed. Drivers must
+ * immediately terminate any operations that will be affected by the
+ * rate change. Callbacks may either return NOTIFY_DONE, NOTIFY_OK,
+ * NOTIFY_STOP or NOTIFY_BAD.
+ *
+ * ABORT_RATE_CHANGE: called if the rate change failed for some reason
+ * after PRE_RATE_CHANGE. In this case, all registered notifiers on
+ * the clk will be called with ABORT_RATE_CHANGE. Callbacks must
+ * always return NOTIFY_DONE or NOTIFY_OK.
+ *
+ * POST_RATE_CHANGE - called after the clk rate change has successfully
+ * completed. Callbacks must always return NOTIFY_DONE or NOTIFY_OK.
+ *
+ */
+#define PRE_RATE_CHANGE BIT(0)
+#define POST_RATE_CHANGE BIT(1)
+#define ABORT_RATE_CHANGE BIT(2)
+
+/**
+ * struct msm_clk_notifier - associate a clk with a notifier
+ * @clk: struct clk * to associate the notifier with
+ * @notifier_head: a blocking_notifier_head for this clk
+ * @node: linked list pointers
+ *
+ * A list of struct clk_notifier is maintained by the notifier code.
+ * An entry is created whenever code registers the first notifier on a
+ * particular @clk. Future notifiers on that @clk are added to the
+ * @notifier_head.
+ */
+struct msm_clk_notifier {
+ struct clk *clk;
+ struct srcu_notifier_head notifier_head;
+ struct list_head node;
+};
+
+/**
+ * struct msm_clk_notifier_data - rate data to pass to the notifier callback
+ * @clk: struct clk * being changed
+ * @old_rate: previous rate of this clk
+ * @new_rate: new rate of this clk
+ *
+ * For a pre-notifier, old_rate is the clk's rate before this rate
+ * change, and new_rate is what the rate will be in the future. For a
+ * post-notifier, old_rate and new_rate are both set to the clk's
+ * current rate (this was done to optimize the implementation).
+ */
+struct msm_clk_notifier_data {
+ struct clk *clk;
+ unsigned long old_rate;
+ unsigned long new_rate;
+};
+
+int msm_clk_notif_register(struct clk *clk, struct notifier_block *nb);
+
+int msm_clk_notif_unregister(struct clk *clk, struct notifier_block *nb);
+
+#endif
diff --git a/include/linux/clk/msm-clock-generic.h b/include/linux/clk/msm-clock-generic.h
new file mode 100644
index 000000000000..6d5468acf744
--- /dev/null
+++ b/include/linux/clk/msm-clock-generic.h
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2013-2015, 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.
+ */
+
+#ifndef __MSM_CLOCK_GENERIC_H
+#define __MSM_CLOCK_GENERIC_H
+
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/of.h>
+
+/**
+ * struct fixed_clk - fixed rate clock
+ * @c: clk
+ */
+struct fixed_clk {
+ struct clk c;
+};
+
+/* ==================== Mux clock ==================== */
+
+struct mux_clk;
+
+struct clk_mux_ops {
+ int (*set_mux_sel)(struct mux_clk *clk, int sel);
+ int (*get_mux_sel)(struct mux_clk *clk);
+
+ /* Optional */
+ bool (*is_enabled)(struct mux_clk *clk);
+ int (*enable)(struct mux_clk *clk);
+ void (*disable)(struct mux_clk *clk);
+ void __iomem *(*list_registers)(struct mux_clk *clk, int n,
+ struct clk_register_data **regs, u32 *size);
+};
+
+#define MUX_SRC_LIST(...) \
+ .parents = (struct clk_src[]){__VA_ARGS__}, \
+ .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__}))
+
+#define MUX_REC_SRC_LIST(...) \
+ .rec_parents = (struct clk * []){__VA_ARGS__}, \
+ .num_rec_parents = ARRAY_SIZE(((struct clk * []){__VA_ARGS__}))
+
+struct mux_clk {
+ /* Parents in decreasing order of preference for obtaining rates. */
+ struct clk_src *parents;
+ int num_parents;
+ /* Recursively search for the requested parent in rec_parents. */
+ struct clk **rec_parents;
+ int num_rec_parents;
+ struct clk *safe_parent;
+ int safe_sel;
+ unsigned long safe_freq;
+ /*
+ * Before attempting a clk_round_rate on available sources, attempt a
+ * clk_get_rate on all those sources. If one of them is already at the
+ * necessary rate, that source will be used.
+ */
+ bool try_get_rate;
+ struct clk_mux_ops *ops;
+ /*
+ * Set if you need the mux to try a new parent before falling back to
+ * the current parent. If the safe_parent field above is set, then the
+ * safe_sel intermediate source will only be used if we fall back to
+ * to the current parent during mux_set_rate.
+ */
+ bool try_new_parent;
+
+ /* Fields not used by helper function. */
+ void *const __iomem *base;
+ u32 offset;
+ u32 en_offset;
+ u32 mask;
+ u32 shift;
+ u32 en_mask;
+ int low_power_sel;
+ void *priv;
+
+ struct clk c;
+};
+
+static inline struct mux_clk *to_mux_clk(struct clk *c)
+{
+ return container_of(c, struct mux_clk, c);
+}
+
+extern struct clk_ops clk_ops_gen_mux;
+
+/* ==================== Divider clock ==================== */
+
+struct div_clk;
+
+struct clk_div_ops {
+ int (*set_div)(struct div_clk *clk, int div);
+ int (*get_div)(struct div_clk *clk);
+ bool (*is_enabled)(struct div_clk *clk);
+ int (*enable)(struct div_clk *clk);
+ void (*disable)(struct div_clk *clk);
+ void __iomem *(*list_registers)(struct div_clk *clk, int n,
+ struct clk_register_data **regs, u32 *size);
+};
+
+struct div_data {
+ unsigned int div;
+ unsigned int min_div;
+ unsigned int max_div;
+ unsigned long rate_margin;
+ /*
+ * Indicate whether this divider clock supports half-interger divider.
+ * If it is, all the min_div and max_div have been doubled. It means
+ * they are 2*N.
+ */
+ bool is_half_divider;
+ /*
+ * Skip odd dividers since the hardware may not support them.
+ */
+ bool skip_odd_div;
+ bool skip_even_div;
+ bool allow_div_one;
+ unsigned int cached_div;
+};
+
+struct div_clk {
+ struct div_data data;
+
+ /*
+ * Some implementations may require the divider to be set to a "safe"
+ * value that allows reprogramming of upstream clocks without violating
+ * voltage constraints.
+ */
+ unsigned long safe_freq;
+
+ /* Optional */
+ struct clk_div_ops *ops;
+
+ /* Fields not used by helper function. */
+ void *const __iomem *base;
+ u32 offset;
+ u32 mask;
+ u32 shift;
+ u32 en_mask;
+ void *priv;
+ struct clk c;
+};
+
+static inline struct div_clk *to_div_clk(struct clk *c)
+{
+ return container_of(c, struct div_clk, c);
+}
+
+extern struct clk_ops clk_ops_div;
+extern struct clk_ops clk_ops_slave_div;
+
+struct ext_clk {
+ struct clk c;
+ struct device *dev;
+ char *clk_id;
+};
+
+long parent_round_rate(struct clk *c, unsigned long rate);
+unsigned long parent_get_rate(struct clk *c);
+int parent_set_rate(struct clk *c, unsigned long rate);
+
+static inline struct ext_clk *to_ext_clk(struct clk *c)
+{
+ return container_of(c, struct ext_clk, c);
+}
+
+extern struct clk_ops clk_ops_ext;
+
+#define DEFINE_FIXED_DIV_CLK(clk_name, _div, _parent) \
+static struct div_clk clk_name = { \
+ .data = { \
+ .max_div = _div, \
+ .min_div = _div, \
+ .div = _div, \
+ }, \
+ .c = { \
+ .parent = _parent, \
+ .dbg_name = #clk_name, \
+ .ops = &clk_ops_div, \
+ CLK_INIT(clk_name.c), \
+ } \
+}
+
+#define DEFINE_FIXED_SLAVE_DIV_CLK(clk_name, _div, _parent) \
+static struct div_clk clk_name = { \
+ .data = { \
+ .max_div = _div, \
+ .min_div = _div, \
+ .div = _div, \
+ }, \
+ .c = { \
+ .parent = _parent, \
+ .dbg_name = #clk_name, \
+ .ops = &clk_ops_slave_div, \
+ CLK_INIT(clk_name.c), \
+ } \
+}
+
+#define DEFINE_EXT_CLK(clk_name, _parent) \
+static struct ext_clk clk_name = { \
+ .c = { \
+ .parent = _parent, \
+ .dbg_name = #clk_name, \
+ .ops = &clk_ops_ext, \
+ CLK_INIT(clk_name.c), \
+ } \
+}
+
+/* ==================== Mux Div clock ==================== */
+
+struct mux_div_clk;
+
+/*
+ * struct mux_div_ops
+ * the enable and disable ops are optional.
+ */
+
+struct mux_div_ops {
+ int (*set_src_div)(struct mux_div_clk *, u32 src_sel, u32 div);
+ void (*get_src_div)(struct mux_div_clk *, u32 *src_sel, u32 *div);
+ int (*enable)(struct mux_div_clk *);
+ void (*disable)(struct mux_div_clk *);
+ bool (*is_enabled)(struct mux_div_clk *);
+ void __iomem *(*list_registers)(struct mux_div_clk *md, int n,
+ struct clk_register_data **regs, u32 *size);
+};
+
+/*
+ * struct mux_div_clk - combined mux/divider clock
+ * @priv
+ parameters needed by ops
+ * @safe_freq
+ when switching rates from A to B, the mux div clock will
+ instead switch from A -> safe_freq -> B. This allows the
+ mux_div clock to change rates while enabled, even if this
+ behavior is not supported by the parent clocks.
+
+ If changing the rate of parent A also causes the rate of
+ parent B to change, then safe_freq must be defined.
+
+ safe_freq is expected to have a source clock which is always
+ on and runs at only one rate.
+ * @parents
+ list of parents and mux indicies
+ * @ops
+ function pointers for hw specific operations
+ * @src_sel
+ the mux index which will be used if the clock is enabled.
+ * @try_get_rate
+ Set if you need the mux to directly jump to a source
+ that is at the desired rate currently.
+ * @force_enable_md
+ Set if the mux-div needs to be force enabled/disabled during
+ clk_enable/disable.
+ */
+
+struct mux_div_clk {
+ /* Required parameters */
+ struct mux_div_ops *ops;
+ struct div_data data;
+ struct clk_src *parents;
+ u32 num_parents;
+
+ struct clk c;
+
+ /* Internal */
+ u32 src_sel;
+
+ /* Optional parameters */
+ void *priv;
+ void __iomem *base;
+ u32 div_mask;
+ u32 div_offset;
+ u32 div_shift;
+ u32 src_mask;
+ u32 src_offset;
+ u32 src_shift;
+ u32 en_mask;
+ u32 en_offset;
+
+ u32 safe_div;
+ struct clk *safe_parent;
+ unsigned long safe_freq;
+ bool try_get_rate;
+ bool force_enable_md;
+};
+
+static inline struct mux_div_clk *to_mux_div_clk(struct clk *clk)
+{
+ return container_of(clk, struct mux_div_clk, c);
+}
+
+extern struct clk_ops clk_ops_mux_div_clk;
+
+#endif
diff --git a/include/linux/clkdev.h b/include/linux/clkdev.h
index 08bffcc466de..0422183013ae 100644
--- a/include/linux/clkdev.h
+++ b/include/linux/clkdev.h
@@ -21,6 +21,7 @@ struct clk_lookup {
struct list_head node;
const char *dev_id;
const char *con_id;
+ int of_idx;
struct clk *clk;
struct clk_hw *clk_hw;
};
diff --git a/include/soc/qcom/clock-alpha-pll.h b/include/soc/qcom/clock-alpha-pll.h
new file mode 100644
index 000000000000..b5a34b4cecb5
--- /dev/null
+++ b/include/soc/qcom/clock-alpha-pll.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012-2015, 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CLOCK_ALPHA_PLL_H
+#define __ARCH_ARM_MACH_MSM_CLOCK_ALPHA_PLL_H
+
+#include <linux/spinlock.h>
+#include <linux/clk/msm-clk-provider.h>
+
+struct alpha_pll_masks {
+ u32 lock_mask; /* lock_det bit */
+ u32 active_mask; /* active_flag in FSM mode */
+ u32 update_mask; /* update bit for dynamic update */
+ u32 vco_mask; /* vco_sel bits */
+ u32 vco_shift;
+ u32 alpha_en_mask; /* alpha_en bit */
+ u32 output_mask; /* pllout_* bits */
+ u32 post_div_mask;
+
+ u32 test_ctl_lo_mask;
+ u32 test_ctl_hi_mask;
+};
+
+struct alpha_pll_vco_tbl {
+ u32 vco_val;
+ unsigned long min_freq;
+ unsigned long max_freq;
+};
+
+#define VCO(a, b, c) { \
+ .vco_val = a,\
+ .min_freq = b,\
+ .max_freq = c,\
+}
+
+struct alpha_pll_clk {
+ struct alpha_pll_masks *masks;
+ void *const __iomem *base;
+ u32 offset;
+
+ /* if fsm_en_mask is set, config PLL to FSM mode */
+ u32 fsm_reg_offset;
+ u32 fsm_en_mask;
+
+ u32 enable_config; /* bitmask of outputs to be enabled */
+ u32 post_div_config; /* masked post divider setting */
+ u32 config_ctl_val; /* config register init value */
+ u32 test_ctl_lo_val; /* test control settings */
+ u32 test_ctl_hi_val;
+
+ struct alpha_pll_vco_tbl *vco_tbl;
+ u32 num_vco;
+ u32 current_vco_val;
+ bool inited;
+ bool slew;
+ bool no_prepared_reconfig;
+
+ /*
+ * Some chipsets need the offline request bit to be
+ * cleared on a second write to the register, even though
+ * SW wants the bit to be set. Set this flag to indicate
+ * that the workaround is required.
+ */
+ bool offline_bit_workaround;
+ bool is_fabia;
+ unsigned long min_supported_freq;
+ struct clk c;
+};
+
+static inline struct alpha_pll_clk *to_alpha_pll_clk(struct clk *c)
+{
+ return container_of(c, struct alpha_pll_clk, c);
+}
+
+
+#endif
+extern void __init_alpha_pll(struct clk *c);
+extern struct clk_ops clk_ops_alpha_pll;
+extern struct clk_ops clk_ops_alpha_pll_hwfsm;
+extern struct clk_ops clk_ops_fixed_alpha_pll;
+extern struct clk_ops clk_ops_dyna_alpha_pll;
+extern struct clk_ops clk_ops_fixed_fabia_alpha_pll;
+extern struct clk_ops clk_ops_fabia_alpha_pll;
diff --git a/include/soc/qcom/clock-local2.h b/include/soc/qcom/clock-local2.h
new file mode 100644
index 000000000000..42c991194391
--- /dev/null
+++ b/include/soc/qcom/clock-local2.h
@@ -0,0 +1,256 @@
+/* Copyright (c) 2012-2015, 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_2_H
+#define __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_2_H
+
+#include <linux/spinlock.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+
+/*
+ * Generic frequency-definition structs and macros
+ */
+
+/**
+ * @freq_hz: output rate
+ * @src_freq: source freq for dynamic pll. For fixed plls, set to 0.
+ * @src_clk: source clock for freq_hz
+ * @m_val: M value corresponding to freq_hz
+ * @n_val: N value corresponding to freq_hz
+ * @d_val: D value corresponding to freq_hz
+ * @div_src_val: Pre divider value and source selection mux index for freq_hz
+ * @sys_vdd: Voltage level required for freq_hz
+ */
+struct clk_freq_tbl {
+ unsigned long freq_hz;
+ unsigned long src_freq;
+ struct clk *src_clk;
+ u32 m_val;
+ u32 n_val;
+ u32 d_val;
+ u32 div_src_val;
+ const unsigned sys_vdd;
+};
+
+#define FREQ_END (ULONG_MAX-1)
+#define F_END { .freq_hz = FREQ_END }
+#define FIXED_CLK_SRC 0
+/*
+ * Generic clock-definition struct and macros
+ */
+/**
+ * struct rcg_clk - root clock generator
+ * @cmd_rcgr_reg: command register
+ * @set_rate: function to set frequency
+ * @freq_tbl: frequency table for this RCG
+ * @current_freq: current RCG frequency
+ * @c: generic clock data
+ * @non_local_children: set if RCG has at least one branch owned by a diff EE
+ * @force_enable_rcgr: set if RCG needs to be force enabled/disabled during
+ * power sequence
+ * @base: pointer to base address of ioremapped registers.
+ */
+struct rcg_clk {
+ u32 cmd_rcgr_reg;
+
+ void (*set_rate)(struct rcg_clk *, struct clk_freq_tbl *);
+
+ struct clk_freq_tbl *freq_tbl;
+ struct clk_freq_tbl *current_freq;
+ struct clk c;
+
+ bool non_local_children;
+ bool force_enable_rcgr;
+ void *const __iomem *base;
+};
+
+static inline struct rcg_clk *to_rcg_clk(struct clk *clk)
+{
+ return container_of(clk, struct rcg_clk, c);
+}
+
+extern struct clk_freq_tbl rcg_dummy_freq;
+
+/**
+ * struct branch_clk - branch clock
+ * @set_rate: Set the frequency of this branch clock.
+ * @c: clk
+ * @cbcr_reg: branch control register
+ * @bcr_reg: block reset register
+ * @has_sibling: true if other branches are derived from this branch's source
+ * @cur_div: current branch divider value
+ * @max_div: maximum branch divider value (if zero, no divider exists)
+ * @halt_check: halt checking type
+ * @toggle_memory: toggle memory during enable/disable if true
+ * @no_halt_check_on_disable: When set, do not check status bit during
+ * clk_disable().
+ * @check_enable_bit: Check the enable bit to determine clock status
+ during handoff.
+ * @base: pointer to base address of ioremapped registers.
+ */
+struct branch_clk {
+ void (*set_rate)(struct branch_clk *, struct clk_freq_tbl *);
+ struct clk c;
+ u32 cbcr_reg;
+ u32 bcr_reg;
+ int has_sibling;
+ u32 cur_div;
+ u32 max_div;
+ const u32 halt_check;
+ bool toggle_memory;
+ bool no_halt_check_on_disable;
+ bool check_enable_bit;
+ void *const __iomem *base;
+};
+
+static inline struct branch_clk *to_branch_clk(struct clk *clk)
+{
+ return container_of(clk, struct branch_clk, c);
+}
+
+/**
+ * struct local_vote_clk - Voteable branch clock
+ * @c: clk
+ * @cbcr_reg: branch control register
+ * @vote_reg: voting register
+ * @en_mask: enable mask
+ * @halt_check: halt checking type
+ * @base: pointer to base address of ioremapped registers.
+ * An on/off switch with a rate derived from the parent.
+ */
+struct local_vote_clk {
+ struct clk c;
+ u32 cbcr_reg;
+ u32 vote_reg;
+ u32 bcr_reg;
+ u32 en_mask;
+ const u32 halt_check;
+ void * __iomem *base;
+};
+
+static inline struct local_vote_clk *to_local_vote_clk(struct clk *clk)
+{
+ return container_of(clk, struct local_vote_clk, c);
+}
+
+/**
+ * struct reset_clk - Reset clock
+ * @c: clk
+ * @reset_reg: block reset register
+ * @base: pointer to base address of ioremapped registers.
+ */
+struct reset_clk {
+ struct clk c;
+ u32 reset_reg;
+ void *__iomem *base;
+};
+
+static inline struct reset_clk *to_reset_clk(struct clk *clk)
+{
+ return container_of(clk, struct reset_clk, c);
+}
+/**
+ * struct measure_clk - for rate measurement debug use
+ * @sample_ticks: sample period in reference clock ticks
+ * @multiplier: measurement scale-up factor
+ * @divider: measurement scale-down factor
+ * @c: clk
+*/
+struct measure_clk {
+ u64 sample_ticks;
+ u32 multiplier;
+ u32 divider;
+
+ struct clk c;
+};
+
+struct measure_clk_data {
+ struct clk *cxo;
+ u32 plltest_reg;
+ u32 plltest_val;
+ u32 xo_div4_cbcr;
+ u32 ctl_reg;
+ u32 status_reg;
+ void *const __iomem *base;
+};
+
+static inline struct measure_clk *to_measure_clk(struct clk *clk)
+{
+ return container_of(clk, struct measure_clk, c);
+}
+
+/**
+ * struct gate_clk
+ * @c: clk
+ * @en_mask: ORed with @en_reg to enable gate clk
+ * @en_reg: register used to enable/disable gate clk
+ * @base: pointer to base address of ioremapped registers
+ */
+struct gate_clk {
+ struct clk c;
+ u32 en_mask;
+ u32 en_reg;
+ unsigned int delay_us;
+ void *const __iomem *base;
+};
+
+static inline struct gate_clk *to_gate_clk(struct clk *clk)
+{
+ return container_of(clk, struct gate_clk, c);
+}
+
+/*
+ * Generic set-rate implementations
+ */
+void set_rate_mnd(struct rcg_clk *clk, struct clk_freq_tbl *nf);
+void set_rate_hid(struct rcg_clk *clk, struct clk_freq_tbl *nf);
+
+/*
+ * Variables from the clock-local driver
+ */
+extern spinlock_t local_clock_reg_lock;
+
+extern struct clk_ops clk_ops_empty;
+extern struct clk_ops clk_ops_rcg;
+extern struct clk_ops clk_ops_rcg_mnd;
+extern struct clk_ops clk_ops_branch;
+extern struct clk_ops clk_ops_vote;
+extern struct clk_ops clk_ops_rcg_hdmi;
+extern struct clk_ops clk_ops_rcg_edp;
+extern struct clk_ops clk_ops_byte;
+extern struct clk_ops clk_ops_pixel;
+extern struct clk_ops clk_ops_byte_multiparent;
+extern struct clk_ops clk_ops_pixel_multiparent;
+extern struct clk_ops clk_ops_edppixel;
+extern struct clk_ops clk_ops_gate;
+extern struct clk_ops clk_ops_rst;
+extern struct clk_mux_ops mux_reg_ops;
+extern struct mux_div_ops rcg_mux_div_ops;
+extern struct clk_div_ops postdiv_reg_ops;
+
+enum handoff pixel_rcg_handoff(struct clk *clk);
+enum handoff byte_rcg_handoff(struct clk *clk);
+unsigned long measure_get_rate(struct clk *c);
+
+/*
+ * Clock definition macros
+ */
+#define DEFINE_CLK_MEASURE(name) \
+ struct clk name = { \
+ .ops = &clk_ops_empty, \
+ .dbg_name = #name, \
+ CLK_INIT(name), \
+ }; \
+
+#endif /* __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_2_H */
diff --git a/include/soc/qcom/clock-pll.h b/include/soc/qcom/clock-pll.h
new file mode 100644
index 000000000000..c17376966a09
--- /dev/null
+++ b/include/soc/qcom/clock-pll.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2012-2015, 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CLOCK_PLL_H
+#define __ARCH_ARM_MACH_MSM_CLOCK_PLL_H
+
+#include <linux/clk/msm-clk-provider.h>
+
+/**
+ * struct pll_freq_tbl - generic PLL frequency definition
+ * @freq_hz: pll frequency in hz
+ * @l_val: pll l value
+ * @m_val: pll m value
+ * @n_val: pll n value
+ * @post_div_val: pll post divider value
+ * @pre_div_val: pll pre-divider value
+ * @vco_val: pll vco value
+ */
+struct pll_freq_tbl {
+ const u32 freq_hz;
+ const u32 l_val;
+ const u32 m_val;
+ const u32 n_val;
+ const u32 post_div_val;
+ const u32 pre_div_val;
+ const u32 vco_val;
+};
+
+/**
+ * struct pll_config_masks - PLL config masks struct
+ * @post_div_mask: mask for post divider bits location
+ * @pre_div_mask: mask for pre-divider bits location
+ * @vco_mask: mask for vco bits location
+ * @mn_en_mask: ORed with pll config register to enable the mn counter
+ * @main_output_mask: ORed with pll config register to enable the main output
+ * @apc_pdn_mask: ORed with pll config register to enable/disable APC PDN
+ * @lock_mask: Mask that indicates that the PLL has locked
+ */
+struct pll_config_masks {
+ u32 apc_pdn_mask;
+ u32 post_div_mask;
+ u32 pre_div_mask;
+ u32 vco_mask;
+ u32 mn_en_mask;
+ u32 main_output_mask;
+ u32 early_output_mask;
+ u32 lock_mask;
+};
+
+struct pll_config_vals {
+ u32 post_div_masked;
+ u32 pre_div_masked;
+ u32 config_ctl_val;
+ u32 config_ctl_hi_val;
+ u32 test_ctl_lo_val;
+ u32 test_ctl_hi_val;
+ u32 alpha_val;
+ bool enable_mn;
+};
+
+struct pll_spm_ctrl {
+ u32 offset;
+ u32 event_bit;
+ void __iomem *spm_base;
+};
+
+#define PLL_FREQ_END (UINT_MAX-1)
+#define PLL_F_END { .freq_hz = PLL_FREQ_END }
+
+/**
+ * struct pll_vote_clk - phase locked loop (HW voteable)
+ * @soft_vote: soft voting variable for multiple PLL software instances
+ * @soft_vote_mask: soft voting mask for multiple PLL software instances
+ * @en_reg: enable register
+ * @en_mask: ORed with @en_reg to enable the clock
+ * @status_mask: ANDed with @status_reg to determine if PLL is active.
+ * @status_reg: status register
+ * @c: clock
+ */
+struct pll_vote_clk {
+ u32 *soft_vote;
+ u32 soft_vote_mask;
+ void __iomem *const en_reg;
+ u32 en_mask;
+ void __iomem *const status_reg;
+ u32 status_mask;
+
+ struct clk c;
+ void *const __iomem *base;
+};
+
+extern struct clk_ops clk_ops_pll_vote;
+extern struct clk_ops clk_ops_pll_acpu_vote;
+extern struct clk_ops clk_ops_pll_sleep_vote;
+
+/* Soft voting values */
+#define PLL_SOFT_VOTE_PRIMARY BIT(0)
+#define PLL_SOFT_VOTE_ACPU BIT(1)
+#define PLL_SOFT_VOTE_AUX BIT(2)
+
+static inline struct pll_vote_clk *to_pll_vote_clk(struct clk *c)
+{
+ return container_of(c, struct pll_vote_clk, c);
+}
+
+/**
+ * struct pll_clk - phase locked loop
+ * @mode_reg: enable register
+ * @l_reg: l value register
+ * @m_reg: m value register
+ * @n_reg: n value register
+ * @config_reg: configuration register, contains mn divider enable, pre divider,
+ * post divider and vco configuration. register name can be configure register
+ * or user_ctl register depending on targets
+ * @config_ctl_reg: "expert" configuration register
+ * @config_ctl_hi_reg: upper 32 bits of the "expert" configuration register
+ * @status_reg: status register, contains the lock detection bit
+ * @init_test_ctl: initialize the test control register
+ * @pgm_test_ctl_enable: program the test_ctl register in the enable sequence
+ * @test_ctl_dbg: if false will configure the test control registers.
+ * @masks: masks used for settings in config_reg
+ * @vals: configuration values to be written to PLL registers
+ * @freq_tbl: pll freq table
+ * @no_prepared_reconfig: Fail round_rate if pll is prepared
+ * @c: clk
+ * @base: pointer to base address of ioremapped registers.
+ */
+struct pll_clk {
+ void __iomem *const mode_reg;
+ void __iomem *const l_reg;
+ void __iomem *const m_reg;
+ void __iomem *const n_reg;
+ void __iomem *const alpha_reg;
+ void __iomem *const config_reg;
+ void __iomem *const config_ctl_reg;
+ void __iomem *const config_ctl_hi_reg;
+ void __iomem *const status_reg;
+ void __iomem *const alt_status_reg;
+ void __iomem *const test_ctl_lo_reg;
+ void __iomem *const test_ctl_hi_reg;
+
+ bool init_test_ctl;
+ bool pgm_test_ctl_enable;
+ bool test_ctl_dbg;
+
+ struct pll_config_masks masks;
+ struct pll_config_vals vals;
+ struct pll_freq_tbl *freq_tbl;
+
+ unsigned long src_rate;
+ unsigned long min_rate;
+ unsigned long max_rate;
+
+ bool inited;
+ bool no_prepared_reconfig;
+
+ struct pll_spm_ctrl spm_ctrl;
+ struct clk c;
+ void *const __iomem *base;
+};
+
+extern struct clk_ops clk_ops_local_pll;
+extern struct clk_ops clk_ops_sr2_pll;
+extern struct clk_ops clk_ops_variable_rate_pll;
+extern struct clk_ops clk_ops_variable_rate_pll_hwfsm;
+
+void __variable_rate_pll_init(struct clk *c);
+
+static inline struct pll_clk *to_pll_clk(struct clk *c)
+{
+ return container_of(c, struct pll_clk, c);
+}
+
+int sr_pll_clk_enable(struct clk *c);
+int sr_hpm_lp_pll_clk_enable(struct clk *c);
+
+struct pll_alt_config {
+ u32 val;
+ u32 mask;
+};
+
+struct pll_config {
+ u32 l;
+ u32 m;
+ u32 n;
+ u32 vco_val;
+ u32 vco_mask;
+ u32 pre_div_val;
+ u32 pre_div_mask;
+ u32 post_div_val;
+ u32 post_div_mask;
+ u32 mn_ena_val;
+ u32 mn_ena_mask;
+ u32 main_output_val;
+ u32 main_output_mask;
+ u32 aux_output_val;
+ u32 aux_output_mask;
+ u32 cfg_ctl_val;
+ /* SR2 PLL specific fields */
+ u32 add_factor_val;
+ u32 add_factor_mask;
+ struct pll_alt_config alt_cfg;
+};
+
+struct pll_config_regs {
+ void __iomem *l_reg;
+ void __iomem *m_reg;
+ void __iomem *n_reg;
+ void __iomem *config_reg;
+ void __iomem *config_alt_reg;
+ void __iomem *config_ctl_reg;
+ void __iomem *mode_reg;
+ void *const __iomem *base;
+};
+
+void configure_sr_pll(struct pll_config *config, struct pll_config_regs *regs,
+ u32 ena_fsm_mode);
+void configure_sr_hpm_lp_pll(struct pll_config *config,
+ struct pll_config_regs *, u32 ena_fsm_mode);
+#endif
diff --git a/include/soc/qcom/clock-rpm.h b/include/soc/qcom/clock-rpm.h
new file mode 100644
index 000000000000..18fedd4c97f7
--- /dev/null
+++ b/include/soc/qcom/clock-rpm.h
@@ -0,0 +1,180 @@
+/* Copyright (c) 2010-2015, 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CLOCK_RPM_H
+#define __ARCH_ARM_MACH_MSM_CLOCK_RPM_H
+
+#include <linux/clk/msm-clk-provider.h>
+#include <soc/qcom/rpm-smd.h>
+
+#define RPM_SMD_KEY_RATE 0x007A484B
+#define RPM_SMD_KEY_ENABLE 0x62616E45
+#define RPM_SMD_KEY_STATE 0x54415453
+
+#define RPM_CLK_BUFFER_A_REQ 0x616B6C63
+#define RPM_KEY_SOFTWARE_ENABLE 0x6E657773
+#define RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370
+
+struct clk_ops;
+struct clk_rpmrs_data;
+extern struct clk_ops clk_ops_rpm;
+extern struct clk_ops clk_ops_rpm_branch;
+
+struct rpm_clk {
+ int rpm_res_type;
+ int rpm_key;
+ int rpm_clk_id;
+ const int rpm_status_id;
+ bool active_only;
+ bool enabled;
+ bool branch; /* true: RPM only accepts 1 for ON and 0 for OFF */
+ struct clk_rpmrs_data *rpmrs_data;
+ struct rpm_clk *peer;
+ struct clk c;
+ uint32_t *last_active_set_vote;
+ uint32_t *last_sleep_set_vote;
+};
+
+static inline struct rpm_clk *to_rpm_clk(struct clk *clk)
+{
+ return container_of(clk, struct rpm_clk, c);
+}
+
+/*
+ * RPM scaling enable function used for target that has an RPM resource for
+ * rpm clock scaling enable.
+ */
+int enable_rpm_scaling(void);
+
+int vote_bimc(struct rpm_clk *r, uint32_t value);
+
+extern struct clk_rpmrs_data clk_rpmrs_data_smd;
+
+/*
+ * A note on name##last_{active,sleep}_set_vote below:
+ * We track the last active and sleep set votes across both
+ * active-only and active+sleep set clocks. We use the same
+ * tracking variables for both clocks in order to keep both
+ * updated about the last vote irrespective of which clock
+ * actually made the request. This is the only way to allow
+ * optimizations that prevent duplicate requests from being sent
+ * to the RPM. Separate tracking does not work since it is not
+ * possible to know if the peer's last request was actually sent
+ * to the RPM.
+ */
+
+#define __DEFINE_CLK_RPM(name, active, type, r_id, stat_id, dep, key, \
+ rpmrsdata) \
+ static struct rpm_clk active; \
+ static uint32_t name##last_active_set_vote; \
+ static uint32_t name##last_sleep_set_vote; \
+ static struct rpm_clk name = { \
+ .rpm_res_type = (type), \
+ .rpm_clk_id = (r_id), \
+ .rpm_status_id = (stat_id), \
+ .rpm_key = (key), \
+ .peer = &active, \
+ .rpmrs_data = (rpmrsdata),\
+ .last_active_set_vote = &name##last_active_set_vote, \
+ .last_sleep_set_vote = &name##last_sleep_set_vote, \
+ .c = { \
+ .ops = &clk_ops_rpm, \
+ .dbg_name = #name, \
+ CLK_INIT(name.c), \
+ .depends = dep, \
+ }, \
+ }; \
+ static struct rpm_clk active = { \
+ .rpm_res_type = (type), \
+ .rpm_clk_id = (r_id), \
+ .rpm_status_id = (stat_id), \
+ .rpm_key = (key), \
+ .peer = &name, \
+ .active_only = true, \
+ .rpmrs_data = (rpmrsdata),\
+ .last_active_set_vote = &name##last_active_set_vote, \
+ .last_sleep_set_vote = &name##last_sleep_set_vote, \
+ .c = { \
+ .ops = &clk_ops_rpm, \
+ .dbg_name = #active, \
+ CLK_INIT(active.c), \
+ .depends = dep, \
+ }, \
+ };
+
+#define __DEFINE_CLK_RPM_BRANCH(name, active, type, r_id, stat_id, r, \
+ key, rpmrsdata) \
+ static struct rpm_clk active; \
+ static uint32_t name##last_active_set_vote; \
+ static uint32_t name##last_sleep_set_vote; \
+ static struct rpm_clk name = { \
+ .rpm_res_type = (type), \
+ .rpm_clk_id = (r_id), \
+ .rpm_status_id = (stat_id), \
+ .rpm_key = (key), \
+ .peer = &active, \
+ .branch = true, \
+ .rpmrs_data = (rpmrsdata),\
+ .last_active_set_vote = &name##last_active_set_vote, \
+ .last_sleep_set_vote = &name##last_sleep_set_vote, \
+ .c = { \
+ .ops = &clk_ops_rpm_branch, \
+ .dbg_name = #name, \
+ .rate = (r), \
+ CLK_INIT(name.c), \
+ }, \
+ }; \
+ static struct rpm_clk active = { \
+ .rpm_res_type = (type), \
+ .rpm_clk_id = (r_id), \
+ .rpm_status_id = (stat_id), \
+ .rpm_key = (key), \
+ .peer = &name, \
+ .active_only = true, \
+ .branch = true, \
+ .rpmrs_data = (rpmrsdata),\
+ .last_active_set_vote = &name##last_active_set_vote, \
+ .last_sleep_set_vote = &name##last_sleep_set_vote, \
+ .c = { \
+ .ops = &clk_ops_rpm_branch, \
+ .dbg_name = #active, \
+ .rate = (r), \
+ CLK_INIT(active.c), \
+ }, \
+ };
+
+#define DEFINE_CLK_RPM_SMD(name, active, type, r_id, dep) \
+ __DEFINE_CLK_RPM(name, active, type, r_id, 0, dep, \
+ RPM_SMD_KEY_RATE, &clk_rpmrs_data_smd)
+
+#define DEFINE_CLK_RPM_SMD_BRANCH(name, active, type, r_id, r) \
+ __DEFINE_CLK_RPM_BRANCH(name, active, type, r_id, 0, r, \
+ RPM_SMD_KEY_ENABLE, &clk_rpmrs_data_smd)
+
+#define DEFINE_CLK_RPM_SMD_QDSS(name, active, type, r_id) \
+ __DEFINE_CLK_RPM(name, active, type, r_id, \
+ 0, 0, RPM_SMD_KEY_STATE, &clk_rpmrs_data_smd)
+/*
+ * The RPM XO buffer clock management code aggregates votes for pin-control mode
+ * and software mode separately. Software-enable has higher priority over pin-
+ * control, and if the software-mode aggregation results in a 'disable', the
+ * buffer will be left in pin-control mode if a pin-control vote is in place.
+ */
+#define DEFINE_CLK_RPM_SMD_XO_BUFFER(name, active, r_id) \
+ __DEFINE_CLK_RPM_BRANCH(name, active, RPM_CLK_BUFFER_A_REQ, r_id, 0, \
+ 1000, RPM_KEY_SOFTWARE_ENABLE, &clk_rpmrs_data_smd)
+
+#define DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(name, active, r_id) \
+ __DEFINE_CLK_RPM_BRANCH(name, active, RPM_CLK_BUFFER_A_REQ, r_id, 0, \
+ 1000, RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY, &clk_rpmrs_data_smd)
+#endif
diff --git a/include/soc/qcom/clock-voter.h b/include/soc/qcom/clock-voter.h
new file mode 100644
index 000000000000..9eb3898db1e8
--- /dev/null
+++ b/include/soc/qcom/clock-voter.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2010-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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H
+#define __ARCH_ARM_MACH_MSM_CLOCK_VOTER_H
+
+#include <linux/clk/msm-clk-provider.h>
+
+struct clk_ops;
+extern struct clk_ops clk_ops_voter;
+
+struct clk_voter {
+ int is_branch;
+ bool enabled;
+ struct clk c;
+};
+
+static inline struct clk_voter *to_clk_voter(struct clk *clk)
+{
+ return container_of(clk, struct clk_voter, c);
+}
+
+#define __DEFINE_CLK_VOTER(clk_name, _parent, _default_rate, _is_branch) \
+ struct clk_voter clk_name = { \
+ .is_branch = (_is_branch), \
+ .c = { \
+ .parent = _parent, \
+ .dbg_name = #clk_name, \
+ .ops = &clk_ops_voter, \
+ .rate = _default_rate, \
+ CLK_INIT(clk_name.c), \
+ }, \
+ }
+
+#define DEFINE_CLK_VOTER(clk_name, _parent, _default_rate) \
+ __DEFINE_CLK_VOTER(clk_name, _parent, _default_rate, 0)
+
+#define DEFINE_CLK_BRANCH_VOTER(clk_name, _parent) \
+ __DEFINE_CLK_VOTER(clk_name, _parent, 1000, 1)
+
+#endif
diff --git a/include/soc/qcom/msm-clock-controller.h b/include/soc/qcom/msm-clock-controller.h
new file mode 100644
index 000000000000..d6bd4e03af0d
--- /dev/null
+++ b/include/soc/qcom/msm-clock-controller.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ */
+
+#ifndef __ARCH_ARM_MSM_CLOCK_CONTROLLER_H
+#define __ARCH_ARM_MSM_CLOCK_CONTROLLER_H
+
+#include <linux/list.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define dt_err(np, fmt, ...) \
+ pr_err("%s: " fmt, np->name, ##__VA_ARGS__)
+#define dt_prop_err(np, str, fmt, ...) \
+ dt_err(np, "%s: " fmt, str, ##__VA_ARGS__)
+
+/**
+ * struct msmclk_parser
+ * @compatible
+ * matches compatible property from devicetree
+ * @parsedt
+ * constructs & returns an instance of the appropriate obj based on
+ * the data from devicetree.
+ */
+struct msmclk_parser {
+ struct list_head list;
+ char *compatible;
+ void * (*parsedt)(struct device *dev, struct device_node *of);
+};
+
+#define MSMCLK_PARSER(fn, str, id) \
+static struct msmclk_parser _msmclk_##fn##id = { \
+ .list = LIST_HEAD_INIT(_msmclk_##fn##id.list), \
+ .compatible = str, \
+ .parsedt = fn, \
+}; \
+static int __init _msmclk_init_##fn##id(void) \
+{ \
+ msmclk_parser_register(&_msmclk_##fn##id); \
+ return 0; \
+} \
+early_initcall(_msmclk_init_##fn##id)
+
+/*
+ * struct msmclk_data
+ * @base
+ * ioremapped region for sub_devices
+ * @list
+ * tracks all registered driver instances
+ * @htable
+ * tracks all registered child clocks
+ * @clk_tbl
+ * array of clk_lookup to be registered with the clock framework
+ */
+#define HASHTABLE_SIZE 200
+struct msmclk_data {
+ void __iomem *base;
+ struct device *dev;
+ struct list_head list;
+ struct hlist_head htable[HASHTABLE_SIZE];
+ struct clk_lookup *clk_tbl;
+ int clk_tbl_size;
+ int max_clk_tbl_size;
+};
+
+#if defined(CONFIG_MSM_CLK_CONTROLLER_V2)
+
+/* Utility functions */
+int of_property_count_phandles(struct device_node *np, char *propname);
+int of_property_read_phandle_index(struct device_node *np, char *propname,
+ int index, phandle *p);
+void *msmclk_generic_clk_init(struct device *dev, struct device_node *np,
+ struct clk *c);
+
+/*
+ * msmclk_parser_register
+ * Registers a parser which will be matched with a node from dt
+ * according to the compatible string.
+ */
+void msmclk_parser_register(struct msmclk_parser *);
+
+/*
+ * msmclk_parse_phandle
+ * On hashtable miss, the corresponding entry will be retrieved from
+ * devicetree, and added to the hashtable.
+ */
+void *msmclk_parse_phandle(struct device *dev, phandle key);
+/*
+ * msmclk_lookup_phandle
+ * Straightforward hashtable lookup
+ */
+void *msmclk_lookup_phandle(struct device *dev, phandle key);
+
+int __init msmclk_init(void);
+#else
+
+static inline int of_property_count_phandles(struct device_node *np,
+ char *propname)
+{
+ return 0;
+}
+
+static inline int of_property_read_phandle_index(struct device_node *np,
+ char *propname, int index, phandle *p)
+{
+ return 0;
+}
+
+static inline void *msmclk_generic_clk_init(struct device *dev,
+ struct device_node *np, struct clk *c)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline void msmclk_parser_register(struct msmclk_parser *p) {};
+
+static inline void *msmclk_parse_phandle(struct device *dev, phandle key)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline void *msmclk_lookup_phandle(struct device *dev, phandle key)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int __init msmclk_init(void)
+{
+ return 0;
+}
+
+#endif /* CONFIG_MSM_CLK_CONTROLLER_V2 */
+#endif /* __ARCH_ARM_MSM_CLOCK_CONTROLLER_H */