summaryrefslogtreecommitdiff
path: root/drivers/clk/qcom/clk-rcg2.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-11-05 12:59:36 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2015-11-05 12:59:36 -0800
commitf66477a0aeb77f97a7de5f791700dadc42f3f792 (patch)
tree0a49febcf211c33948be618860eb3d545b7c6608 /drivers/clk/qcom/clk-rcg2.c
parent400c5bd5a5b1faf3089322ace58b974446a8ddc3 (diff)
parente5bf1991ea62b4f4fc906d0828f7eed988fc3835 (diff)
Merge tag 'clk-for-linus-20151104' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd: "The majority of the changes are driver updates and new device support. The core framework is mostly unchanged this time around, with only a couple patches to expose a clk provider API and make getting clk parent names from DT more robust. Driver updates: - Support for clock controllers found on Broadcom Northstar SoCs and bcm2835 SoC - Support for Allwinner audio clocks - A few cleanup patches for Tegra drivers and support for the highest DFLL frequencies on Tegra124 - Samsung exynos7 fixes and improvements - i.Mx SoC updates to add a few missing clocks and keep debug uart clocks on during kernel intialization - Some mediatek cleanups and support for more subsystem clocks - Support for msm8916 gpu/audio clocks and qcom's GDSC power domain controllers - A new driver for the Silabs si514 clock chip" * tag 'clk-for-linus-20151104' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (143 commits) clk: qcom: msm8960: Fix dsi1/2 halt bits clk: lpc18xx-cgu: fix potential system hang when disabling unused clocks clk: lpc18xx-ccu: fix potential system hang when disabling unused clocks clk: Add clk_hw_is_enabled() for use by clk providers clk: Add stubs for of_clk_*() APIs when CONFIG_OF=n clk: versatile-icst: fix memory leak clk: Remove clk_{register,unregister}_multiplier() clk: iproc: define Broadcom NS2 iProc clock binding clk: iproc: define Broadcom NSP iProc clock binding clk: ns2: add clock support for Broadcom Northstar 2 SoC clk: iproc: Separate status and control variables clk: iproc: Split off dig_filter clk: iproc: Add PLL base write function clk: nsp: add clock support for Broadcom Northstar Plus SoC clk: iproc: Add PWRCTRL support clk: cygnus: Convert all macros to all caps ARM: cygnus: fix link failures when CONFIG_COMMON_CLK_IPROC is disabled clk: imx31: add missing of_node_put clk: imx27: add missing of_node_put clk: si5351: add missing of_node_put ...
Diffstat (limited to 'drivers/clk/qcom/clk-rcg2.c')
-rw-r--r--drivers/clk/qcom/clk-rcg2.c170
1 files changed, 162 insertions, 8 deletions
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 9aec1761fd29..b544bb302f79 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -300,6 +300,85 @@ const struct clk_ops clk_rcg2_ops = {
};
EXPORT_SYMBOL_GPL(clk_rcg2_ops);
+static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const char *name = clk_hw_get_name(hw);
+ int ret, count;
+
+ /* force enable RCG */
+ ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
+ CMD_ROOT_EN, CMD_ROOT_EN);
+ if (ret)
+ return ret;
+
+ /* wait for RCG to turn ON */
+ for (count = 500; count > 0; count--) {
+ ret = clk_rcg2_is_enabled(hw);
+ if (ret)
+ break;
+ udelay(1);
+ }
+ if (!count)
+ pr_err("%s: RCG did not turn on\n", name);
+
+ /* set clock rate */
+ ret = __clk_rcg2_set_rate(hw, rate);
+ if (ret)
+ return ret;
+
+ /* clear force enable RCG */
+ return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
+ CMD_ROOT_EN, 0);
+}
+
+static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+ /* cache the rate */
+ rcg->current_freq = rate;
+
+ if (!__clk_is_enabled(hw->clk))
+ return 0;
+
+ return clk_rcg2_shared_force_enable(hw, rcg->current_freq);
+}
+
+static unsigned long
+clk_rcg2_shared_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+ return rcg->current_freq = clk_rcg2_recalc_rate(hw, parent_rate);
+}
+
+static int clk_rcg2_shared_enable(struct clk_hw *hw)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+ return clk_rcg2_shared_force_enable(hw, rcg->current_freq);
+}
+
+static void clk_rcg2_shared_disable(struct clk_hw *hw)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+
+ /* switch to XO, which is the lowest entry in the freq table */
+ clk_rcg2_shared_set_rate(hw, rcg->freq_tbl[0].freq, 0);
+}
+
+const struct clk_ops clk_rcg2_shared_ops = {
+ .enable = clk_rcg2_shared_enable,
+ .disable = clk_rcg2_shared_disable,
+ .get_parent = clk_rcg2_get_parent,
+ .recalc_rate = clk_rcg2_shared_recalc_rate,
+ .determine_rate = clk_rcg2_determine_rate,
+ .set_rate = clk_rcg2_shared_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops);
+
struct frac_entry {
int num;
int den;
@@ -485,6 +564,76 @@ const struct clk_ops clk_byte_ops = {
};
EXPORT_SYMBOL_GPL(clk_byte_ops);
+static int clk_byte2_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ unsigned long parent_rate, div;
+ u32 mask = BIT(rcg->hid_width) - 1;
+ struct clk_hw *p;
+ unsigned long rate = req->rate;
+
+ if (rate == 0)
+ return -EINVAL;
+
+ p = req->best_parent_hw;
+ req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate);
+
+ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
+ div = min_t(u32, div, mask);
+
+ req->rate = calc_rate(parent_rate, 0, 0, 0, div);
+
+ return 0;
+}
+
+static int clk_byte2_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ struct freq_tbl f = { 0 };
+ unsigned long div;
+ int i, num_parents = clk_hw_get_num_parents(hw);
+ u32 mask = BIT(rcg->hid_width) - 1;
+ u32 cfg;
+
+ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
+ div = min_t(u32, div, mask);
+
+ f.pre_div = div;
+
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
+ cfg &= CFG_SRC_SEL_MASK;
+ cfg >>= CFG_SRC_SEL_SHIFT;
+
+ for (i = 0; i < num_parents; i++) {
+ if (cfg == rcg->parent_map[i].cfg) {
+ f.src = rcg->parent_map[i].src;
+ return clk_rcg2_configure(rcg, &f);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int clk_byte2_set_rate_and_parent(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate, u8 index)
+{
+ /* Read the hardware to determine parent during set_rate */
+ return clk_byte2_set_rate(hw, rate, parent_rate);
+}
+
+const struct clk_ops clk_byte2_ops = {
+ .is_enabled = clk_rcg2_is_enabled,
+ .get_parent = clk_rcg2_get_parent,
+ .set_parent = clk_rcg2_set_parent,
+ .recalc_rate = clk_rcg2_recalc_rate,
+ .set_rate = clk_byte2_set_rate,
+ .set_rate_and_parent = clk_byte2_set_rate_and_parent,
+ .determine_rate = clk_byte2_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_byte2_ops);
+
static const struct frac_entry frac_table_pixel[] = {
{ 3, 8 },
{ 2, 9 },
@@ -496,14 +645,9 @@ static const struct frac_entry frac_table_pixel[] = {
static int clk_pixel_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
- struct clk_rcg2 *rcg = to_clk_rcg2(hw);
unsigned long request, src_rate;
int delta = 100000;
- const struct freq_tbl *f = rcg->freq_tbl;
const struct frac_entry *frac = frac_table_pixel;
- int index = qcom_find_src_index(hw, rcg->parent_map, f->src);
-
- req->best_parent_hw = clk_hw_get_parent_by_index(hw, index);
for (; frac->num; frac++) {
request = (req->rate * frac->den) / frac->num;
@@ -525,12 +669,23 @@ static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
- struct freq_tbl f = *rcg->freq_tbl;
+ struct freq_tbl f = { 0 };
const struct frac_entry *frac = frac_table_pixel;
unsigned long request;
int delta = 100000;
u32 mask = BIT(rcg->hid_width) - 1;
- u32 hid_div;
+ u32 hid_div, cfg;
+ int i, num_parents = clk_hw_get_num_parents(hw);
+
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
+ cfg &= CFG_SRC_SEL_MASK;
+ cfg >>= CFG_SRC_SEL_SHIFT;
+
+ for (i = 0; i < num_parents; i++)
+ if (cfg == rcg->parent_map[i].cfg) {
+ f.src = rcg->parent_map[i].src;
+ break;
+ }
for (; frac->num; frac++) {
request = (rate * frac->den) / frac->num;
@@ -555,7 +710,6 @@ static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate,
static int clk_pixel_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate, u8 index)
{
- /* Parent index is set statically in frequency table */
return clk_pixel_set_rate(hw, rate, parent_rate);
}