openwrtv4/target/linux/ipq806x/patches/0105-clk-qcom-Support-display-RCG-clocks.patch
John Crispin 3c1f6e358d ipq806x: Add support for IPQ806x chip family
Patches are generated using the "format-patch" command from the
following location:
*https://www.codeaurora.org/cgit/quic/kernel/galak-msm/log/?h=apq_ipq_base
*rev=0771849495b4128cac2faf7d49c85c729fc48b20
Patches numbered 76/77/102/103 have already been integrated in 3.14.12,
so they're not in this list.

All these patches are either integrated are pending integration into
kernel.org, therefore these patches should go away once the kernel
gets upgraded to 3.16.

Support is currently limited to AP148 board but can be extended to other
platforms in the future.

These changes do not cover ethernet connectivity.

Signed-off-by: Mathieu Olivari <mathieu@codeaurora.org>

SVN-Revision: 42334
2014-08-30 09:32:58 +00:00

378 lines
10 KiB
Diff

From 3123079878e29eb8c541111e30de4d1bb42ac6f9 Mon Sep 17 00:00:00 2001
From: Stephen Boyd <sboyd@codeaurora.org>
Date: Fri, 16 May 2014 16:07:11 -0700
Subject: [PATCH 105/182] clk: qcom: Support display RCG clocks
Add support for the DSI/EDP/HDMI RCG clocks. With the proper
display driver in place this should allow us to support display
clocks on msm8974 based devices.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Mike Turquette <mturquette@linaro.org>
---
drivers/clk/qcom/clk-rcg.h | 3 +
drivers/clk/qcom/clk-rcg2.c | 299 ++++++++++++++++++++++++++++++++++++++++---
2 files changed, 287 insertions(+), 15 deletions(-)
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index 1d6b6de..b9ec11d 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -155,5 +155,8 @@ struct clk_rcg2 {
#define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr)
extern const struct clk_ops clk_rcg2_ops;
+extern const struct clk_ops clk_edp_pixel_ops;
+extern const struct clk_ops clk_byte_ops;
+extern const struct clk_ops clk_pixel_ops;
#endif
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index cbecaec..cd185d5 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -19,6 +19,7 @@
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/regmap.h>
+#include <linux/math64.h>
#include <asm/div64.h>
@@ -225,31 +226,25 @@ static long clk_rcg2_determine_rate(struct clk_hw *hw, unsigned long rate,
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, rate, p_rate, p);
}
-static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
+static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
{
- struct clk_rcg2 *rcg = to_clk_rcg2(hw);
- const struct freq_tbl *f;
u32 cfg, mask;
int ret;
- f = find_freq(rcg->freq_tbl, rate);
- if (!f)
- return -EINVAL;
-
if (rcg->mnd_width && f->n) {
mask = BIT(rcg->mnd_width) - 1;
- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG,
- mask, f->m);
+ ret = regmap_update_bits(rcg->clkr.regmap,
+ rcg->cmd_rcgr + M_REG, mask, f->m);
if (ret)
return ret;
- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG,
- mask, ~(f->n - f->m));
+ ret = regmap_update_bits(rcg->clkr.regmap,
+ rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m));
if (ret)
return ret;
- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + D_REG,
- mask, ~f->n);
+ ret = regmap_update_bits(rcg->clkr.regmap,
+ rcg->cmd_rcgr + D_REG, mask, ~f->n);
if (ret)
return ret;
}
@@ -260,14 +255,26 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
cfg |= rcg->parent_map[f->src] << CFG_SRC_SEL_SHIFT;
if (rcg->mnd_width && f->n)
cfg |= CFG_MODE_DUAL_EDGE;
- ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, mask,
- cfg);
+ ret = regmap_update_bits(rcg->clkr.regmap,
+ rcg->cmd_rcgr + CFG_REG, mask, cfg);
if (ret)
return ret;
return update_config(rcg);
}
+static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const struct freq_tbl *f;
+
+ f = find_freq(rcg->freq_tbl, rate);
+ if (!f)
+ return -EINVAL;
+
+ return clk_rcg2_configure(rcg, f);
+}
+
static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
@@ -290,3 +297,265 @@ const struct clk_ops clk_rcg2_ops = {
.set_rate_and_parent = clk_rcg2_set_rate_and_parent,
};
EXPORT_SYMBOL_GPL(clk_rcg2_ops);
+
+struct frac_entry {
+ int num;
+ int den;
+};
+
+static const 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 */
+ { },
+};
+
+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 */
+ { },
+};
+
+static int clk_edp_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;
+ const struct frac_entry *frac;
+ int delta = 100000;
+ s64 src_rate = parent_rate;
+ s64 request;
+ u32 mask = BIT(rcg->hid_width) - 1;
+ u32 hid_div;
+
+ if (src_rate == 810000000)
+ frac = frac_table_810m;
+ else
+ frac = frac_table_675m;
+
+ for (; frac->num; frac++) {
+ request = rate;
+ request *= frac->den;
+ request = div_s64(request, frac->num);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
+ &hid_div);
+ f.pre_div = hid_div;
+ f.pre_div >>= CFG_SRC_DIV_SHIFT;
+ f.pre_div &= mask;
+ f.m = frac->num;
+ f.n = frac->den;
+
+ return clk_rcg2_configure(rcg, &f);
+ }
+
+ return -EINVAL;
+}
+
+static int clk_edp_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_edp_pixel_set_rate(hw, rate, parent_rate);
+}
+
+static long clk_edp_pixel_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const struct freq_tbl *f = rcg->freq_tbl;
+ const struct frac_entry *frac;
+ int delta = 100000;
+ s64 src_rate = *p_rate;
+ s64 request;
+ u32 mask = BIT(rcg->hid_width) - 1;
+ u32 hid_div;
+
+ /* Force the correct parent */
+ *p = clk_get_parent_by_index(hw->clk, f->src);
+
+ if (src_rate == 810000000)
+ frac = frac_table_810m;
+ else
+ frac = frac_table_675m;
+
+ for (; frac->num; frac++) {
+ request = rate;
+ request *= frac->den;
+ request = div_s64(request, frac->num);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
+ &hid_div);
+ hid_div >>= CFG_SRC_DIV_SHIFT;
+ hid_div &= mask;
+
+ return calc_rate(src_rate, frac->num, frac->den, !!frac->den,
+ hid_div);
+ }
+
+ return -EINVAL;
+}
+
+const struct clk_ops clk_edp_pixel_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_edp_pixel_set_rate,
+ .set_rate_and_parent = clk_edp_pixel_set_rate_and_parent,
+ .determine_rate = clk_edp_pixel_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_edp_pixel_ops);
+
+static long clk_byte_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+ const struct freq_tbl *f = rcg->freq_tbl;
+ unsigned long parent_rate, div;
+ u32 mask = BIT(rcg->hid_width) - 1;
+
+ if (rate == 0)
+ return -EINVAL;
+
+ *p = clk_get_parent_by_index(hw->clk, f->src);
+ *p_rate = parent_rate = __clk_round_rate(*p, rate);
+
+ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
+ div = min_t(u32, div, mask);
+
+ return calc_rate(parent_rate, 0, 0, 0, div);
+}
+
+static int clk_byte_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;
+ unsigned long div;
+ u32 mask = BIT(rcg->hid_width) - 1;
+
+ div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
+ div = min_t(u32, div, mask);
+
+ f.pre_div = div;
+
+ return clk_rcg2_configure(rcg, &f);
+}
+
+static int clk_byte_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_byte_set_rate(hw, rate, parent_rate);
+}
+
+const struct clk_ops clk_byte_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_byte_set_rate,
+ .set_rate_and_parent = clk_byte_set_rate_and_parent,
+ .determine_rate = clk_byte_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_byte_ops);
+
+static const struct frac_entry frac_table_pixel[] = {
+ { 3, 8 },
+ { 2, 9 },
+ { 4, 9 },
+ { 1, 1 },
+ { }
+};
+
+static long clk_pixel_determine_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *p_rate, struct clk **p)
+{
+ 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;
+ struct clk *parent = *p = clk_get_parent_by_index(hw->clk, f->src);
+
+ for (; frac->num; frac++) {
+ request = (rate * frac->den) / frac->num;
+
+ src_rate = __clk_round_rate(parent, request);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ *p_rate = src_rate;
+ return (src_rate * frac->num) / frac->den;
+ }
+
+ return -EINVAL;
+}
+
+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;
+ const struct frac_entry *frac = frac_table_pixel;
+ unsigned long request, src_rate;
+ int delta = 100000;
+ u32 mask = BIT(rcg->hid_width) - 1;
+ u32 hid_div;
+ struct clk *parent = clk_get_parent_by_index(hw->clk, f.src);
+
+ for (; frac->num; frac++) {
+ request = (rate * frac->den) / frac->num;
+
+ src_rate = __clk_round_rate(parent, request);
+ if ((src_rate < (request - delta)) ||
+ (src_rate > (request + delta)))
+ continue;
+
+ regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
+ &hid_div);
+ f.pre_div = hid_div;
+ f.pre_div >>= CFG_SRC_DIV_SHIFT;
+ f.pre_div &= mask;
+ f.m = frac->num;
+ f.n = frac->den;
+
+ return clk_rcg2_configure(rcg, &f);
+ }
+ return -EINVAL;
+}
+
+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);
+}
+
+const struct clk_ops clk_pixel_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_pixel_set_rate,
+ .set_rate_and_parent = clk_pixel_set_rate_and_parent,
+ .determine_rate = clk_pixel_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_pixel_ops);
--
1.7.10.4