From ea08eeccae9297f7aabd2ef7f0c2517ac4549acc Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Tue, 20 Feb 2024 01:18:26 +0000
Subject: [PATCH] write in 30M
---
kernel/drivers/clk/qcom/clk-alpha-pll.c | 537 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 493 insertions(+), 44 deletions(-)
diff --git a/kernel/drivers/clk/qcom/clk-alpha-pll.c b/kernel/drivers/clk/qcom/clk-alpha-pll.c
index a91d97c..cf265ab 100644
--- a/kernel/drivers/clk/qcom/clk-alpha-pll.c
+++ b/kernel/drivers/clk/qcom/clk-alpha-pll.c
@@ -32,6 +32,7 @@
# define PLL_LOCK_DET BIT(31)
#define PLL_L_VAL(p) ((p)->offset + (p)->regs[PLL_OFF_L_VAL])
+#define PLL_CAL_L_VAL(p) ((p)->offset + (p)->regs[PLL_OFF_CAL_L_VAL])
#define PLL_ALPHA_VAL(p) ((p)->offset + (p)->regs[PLL_OFF_ALPHA_VAL])
#define PLL_ALPHA_VAL_U(p) ((p)->offset + (p)->regs[PLL_OFF_ALPHA_VAL_U])
@@ -44,11 +45,14 @@
# define PLL_VCO_MASK 0x3
#define PLL_USER_CTL_U(p) ((p)->offset + (p)->regs[PLL_OFF_USER_CTL_U])
+#define PLL_USER_CTL_U1(p) ((p)->offset + (p)->regs[PLL_OFF_USER_CTL_U1])
#define PLL_CONFIG_CTL(p) ((p)->offset + (p)->regs[PLL_OFF_CONFIG_CTL])
#define PLL_CONFIG_CTL_U(p) ((p)->offset + (p)->regs[PLL_OFF_CONFIG_CTL_U])
+#define PLL_CONFIG_CTL_U1(p) ((p)->offset + (p)->regs[PLL_OFF_CONFIG_CTL_U1])
#define PLL_TEST_CTL(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL])
#define PLL_TEST_CTL_U(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL_U])
+#define PLL_TEST_CTL_U1(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL_U1])
#define PLL_STATUS(p) ((p)->offset + (p)->regs[PLL_OFF_STATUS])
#define PLL_OPMODE(p) ((p)->offset + (p)->regs[PLL_OFF_OPMODE])
#define PLL_FRAC(p) ((p)->offset + (p)->regs[PLL_OFF_FRAC])
@@ -96,6 +100,22 @@
[PLL_OFF_OPMODE] = 0x2c,
[PLL_OFF_FRAC] = 0x38,
},
+ [CLK_ALPHA_PLL_TYPE_TRION] = {
+ [PLL_OFF_L_VAL] = 0x04,
+ [PLL_OFF_CAL_L_VAL] = 0x08,
+ [PLL_OFF_USER_CTL] = 0x0c,
+ [PLL_OFF_USER_CTL_U] = 0x10,
+ [PLL_OFF_USER_CTL_U1] = 0x14,
+ [PLL_OFF_CONFIG_CTL] = 0x18,
+ [PLL_OFF_CONFIG_CTL_U] = 0x1c,
+ [PLL_OFF_CONFIG_CTL_U1] = 0x20,
+ [PLL_OFF_TEST_CTL] = 0x24,
+ [PLL_OFF_TEST_CTL_U] = 0x28,
+ [PLL_OFF_TEST_CTL_U1] = 0x2c,
+ [PLL_OFF_STATUS] = 0x30,
+ [PLL_OFF_OPMODE] = 0x38,
+ [PLL_OFF_ALPHA_VAL] = 0x40,
+ },
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);
@@ -114,11 +134,17 @@
#define PLL_HUAYRA_N_MASK 0xff
#define PLL_HUAYRA_ALPHA_WIDTH 16
-#define FABIA_OPMODE_STANDBY 0x0
-#define FABIA_OPMODE_RUN 0x1
+#define PLL_STANDBY 0x0
+#define PLL_RUN 0x1
+#define PLL_OUT_MASK 0x7
+#define PLL_RATE_MARGIN 500
-#define FABIA_PLL_OUT_MASK 0x7
-#define FABIA_PLL_RATE_MARGIN 500
+/* TRION PLL specific settings and offsets */
+#define TRION_PLL_CAL_VAL 0x44
+#define TRION_PCAL_DONE BIT(26)
+
+/* LUCID PLL specific settings and offsets */
+#define LUCID_PCAL_DONE BIT(27)
#define pll_alpha_width(p) \
((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \
@@ -220,6 +246,7 @@
if (pll->flags & SUPPORTS_FSM_MODE)
qcom_pll_set_fsm_mode(regmap, PLL_MODE(pll), 6, 0);
}
+EXPORT_SYMBOL_GPL(clk_alpha_pll_configure);
static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
{
@@ -519,7 +546,8 @@
rate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
vco = alpha_pll_find_vco(pll, rate);
if (pll->vco_table && !vco) {
- pr_err("alpha pll not in a valid vco range\n");
+ pr_err("%s: alpha pll not in a valid vco range\n",
+ clk_hw_get_name(hw));
return -EINVAL;
}
@@ -581,7 +609,7 @@
alpha_huayra_pll_calc_rate(u64 prate, u32 l, u32 a)
{
/*
- * a contains 16 bit alpha_val in two’s compliment number in the range
+ * a contains 16 bit alpha_val in two’s complement number in the range
* of [-0.5, 0.5).
*/
if (a >= BIT(PLL_HUAYRA_ALPHA_WIDTH - 1))
@@ -613,7 +641,7 @@
quotient++;
/*
- * alpha_val should be in two’s compliment number in the range
+ * alpha_val should be in two’s complement number in the range
* of [-0.5, 0.5) so if quotient >= 0.5 then increment the l value
* since alpha value will be subtracted in this case.
*/
@@ -638,7 +666,7 @@
regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL(pll), &alpha);
/*
* Depending upon alpha_mode, it can be treated as M/N value or
- * as a two’s compliment number. When alpha_mode=1,
+ * as a two’s complement number. When alpha_mode=1,
* pll_alpha_val<15:8>=M and pll_apla_val<7:0>=N
*
* Fout=FIN*(L+(M/N))
@@ -646,12 +674,12 @@
* M is a signed number (-128 to 127) and N is unsigned
* (0 to 255). M/N has to be within +/-0.5.
*
- * When alpha_mode=0, it is a two’s compliment number in the
+ * When alpha_mode=0, it is a two’s complement number in the
* range [-0.5, 0.5).
*
* Fout=FIN*(L+(alpha_val)/2^16)
*
- * where alpha_val is two’s compliment number.
+ * where alpha_val is two’s complement number.
*/
if (!(ctl & PLL_ALPHA_MODE))
return alpha_huayra_pll_calc_rate(rate, l, alpha);
@@ -697,7 +725,7 @@
*/
if (clk_alpha_pll_is_enabled(hw)) {
if (cur_alpha != a) {
- pr_err("clock needs to be gated %s\n",
+ pr_err("%s: clock needs to be gated\n",
clk_hw_get_name(hw));
return -EBUSY;
}
@@ -729,6 +757,117 @@
return alpha_huayra_pll_round_rate(rate, *prate, &l, &a);
}
+static int trion_pll_is_enabled(struct clk_alpha_pll *pll,
+ struct regmap *regmap)
+{
+ u32 mode_regval, opmode_regval;
+ int ret;
+
+ ret = regmap_read(regmap, PLL_MODE(pll), &mode_regval);
+ ret |= regmap_read(regmap, PLL_OPMODE(pll), &opmode_regval);
+ if (ret)
+ return 0;
+
+ return ((opmode_regval & PLL_RUN) && (mode_regval & PLL_OUTCTRL));
+}
+
+static int clk_trion_pll_is_enabled(struct clk_hw *hw)
+{
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+
+ return trion_pll_is_enabled(pll, pll->clkr.regmap);
+}
+
+static int clk_trion_pll_enable(struct clk_hw *hw)
+{
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+ struct regmap *regmap = pll->clkr.regmap;
+ u32 val;
+ int ret;
+
+ ret = regmap_read(regmap, PLL_MODE(pll), &val);
+ if (ret)
+ return ret;
+
+ /* If in FSM mode, just vote for it */
+ if (val & PLL_VOTE_FSM_ENA) {
+ ret = clk_enable_regmap(hw);
+ if (ret)
+ return ret;
+ return wait_for_pll_enable_active(pll);
+ }
+
+ /* Set operation mode to RUN */
+ regmap_write(regmap, PLL_OPMODE(pll), PLL_RUN);
+
+ ret = wait_for_pll_enable_lock(pll);
+ if (ret)
+ return ret;
+
+ /* Enable the PLL outputs */
+ ret = regmap_update_bits(regmap, PLL_USER_CTL(pll),
+ PLL_OUT_MASK, PLL_OUT_MASK);
+ if (ret)
+ return ret;
+
+ /* Enable the global PLL outputs */
+ return regmap_update_bits(regmap, PLL_MODE(pll),
+ PLL_OUTCTRL, PLL_OUTCTRL);
+}
+
+static void clk_trion_pll_disable(struct clk_hw *hw)
+{
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+ struct regmap *regmap = pll->clkr.regmap;
+ u32 val;
+ int ret;
+
+ ret = regmap_read(regmap, PLL_MODE(pll), &val);
+ if (ret)
+ return;
+
+ /* If in FSM mode, just unvote it */
+ if (val & PLL_VOTE_FSM_ENA) {
+ clk_disable_regmap(hw);
+ return;
+ }
+
+ /* Disable the global PLL output */
+ ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0);
+ if (ret)
+ return;
+
+ /* Disable the PLL outputs */
+ ret = regmap_update_bits(regmap, PLL_USER_CTL(pll),
+ PLL_OUT_MASK, 0);
+ if (ret)
+ return;
+
+ /* Place the PLL mode in STANDBY */
+ regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY);
+ regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N);
+}
+
+static unsigned long
+clk_trion_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+ u32 l, frac, alpha_width = pll_alpha_width(pll);
+
+ regmap_read(pll->clkr.regmap, PLL_L_VAL(pll), &l);
+ regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL(pll), &frac);
+
+ return alpha_pll_calc_rate(parent_rate, l, frac, alpha_width);
+}
+
+const struct clk_ops clk_alpha_pll_fixed_ops = {
+ .enable = clk_alpha_pll_enable,
+ .disable = clk_alpha_pll_disable,
+ .is_enabled = clk_alpha_pll_is_enabled,
+ .recalc_rate = clk_alpha_pll_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_fixed_ops);
+
const struct clk_ops clk_alpha_pll_ops = {
.enable = clk_alpha_pll_enable,
.disable = clk_alpha_pll_disable,
@@ -758,6 +897,15 @@
.set_rate = clk_alpha_pll_hwfsm_set_rate,
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops);
+
+const struct clk_ops clk_alpha_pll_fixed_trion_ops = {
+ .enable = clk_trion_pll_enable,
+ .disable = clk_trion_pll_disable,
+ .is_enabled = clk_trion_pll_is_enabled,
+ .recalc_rate = clk_trion_pll_recalc_rate,
+ .round_rate = clk_alpha_pll_round_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_fixed_trion_ops);
static unsigned long
clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
@@ -831,7 +979,7 @@
int div;
/* 16 -> 0xf, 8 -> 0x7, 4 -> 0x3, 2 -> 0x1, 1 -> 0x0 */
- div = DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;
+ div = DIV_ROUND_UP_ULL(parent_rate, rate) - 1;
return regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
PLL_POST_DIV_MASK(pll) << PLL_POST_DIV_SHIFT,
@@ -865,6 +1013,25 @@
if (config->config_ctl_val)
regmap_write(regmap, PLL_CONFIG_CTL(pll),
config->config_ctl_val);
+
+ if (config->config_ctl_hi_val)
+ regmap_write(regmap, PLL_CONFIG_CTL_U(pll),
+ config->config_ctl_hi_val);
+
+ if (config->user_ctl_val)
+ regmap_write(regmap, PLL_USER_CTL(pll), config->user_ctl_val);
+
+ if (config->user_ctl_hi_val)
+ regmap_write(regmap, PLL_USER_CTL_U(pll),
+ config->user_ctl_hi_val);
+
+ if (config->test_ctl_val)
+ regmap_write(regmap, PLL_TEST_CTL(pll),
+ config->test_ctl_val);
+
+ if (config->test_ctl_hi_val)
+ regmap_write(regmap, PLL_TEST_CTL_U(pll),
+ config->test_ctl_hi_val);
if (config->post_div_mask) {
mask = config->post_div_mask;
@@ -903,14 +1070,14 @@
return ret;
/* Skip If PLL is already running */
- if ((opmode_val & FABIA_OPMODE_RUN) && (val & PLL_OUTCTRL))
+ if ((opmode_val & PLL_RUN) && (val & PLL_OUTCTRL))
return 0;
ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0);
if (ret)
return ret;
- ret = regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_STANDBY);
+ ret = regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY);
if (ret)
return ret;
@@ -919,7 +1086,7 @@
if (ret)
return ret;
- ret = regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_RUN);
+ ret = regmap_write(regmap, PLL_OPMODE(pll), PLL_RUN);
if (ret)
return ret;
@@ -928,7 +1095,7 @@
return ret;
ret = regmap_update_bits(regmap, PLL_USER_CTL(pll),
- FABIA_PLL_OUT_MASK, FABIA_PLL_OUT_MASK);
+ PLL_OUT_MASK, PLL_OUT_MASK);
if (ret)
return ret;
@@ -958,13 +1125,12 @@
return;
/* Disable main outputs */
- ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), FABIA_PLL_OUT_MASK,
- 0);
+ ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), PLL_OUT_MASK, 0);
if (ret)
return;
/* Place the PLL in STANDBY */
- regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_STANDBY);
+ regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY);
}
static unsigned long alpha_pll_fabia_recalc_rate(struct clk_hw *hw,
@@ -983,14 +1149,9 @@
unsigned long prate)
{
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
- u32 val, l, alpha_width = pll_alpha_width(pll);
+ u32 l, alpha_width = pll_alpha_width(pll);
u64 a;
- unsigned long rrate;
- int ret = 0;
-
- ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val);
- if (ret)
- return ret;
+ unsigned long rrate, max = rate + PLL_RATE_MARGIN;
rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
@@ -998,8 +1159,9 @@
* Due to limited number of bits for fractional rate programming, the
* rounded up rate could be marginally higher than the requested rate.
*/
- if (rrate > (rate + FABIA_PLL_RATE_MARGIN) || rrate < rate) {
- pr_err("Call set rate on the PLL with rounded rates!\n");
+ if (rrate > (rate + PLL_RATE_MARGIN) || rrate < rate) {
+ pr_err("%s: Rounded rate %lu not within range [%lu, %lu)\n",
+ clk_hw_get_name(hw), rrate, rate, max);
return -EINVAL;
}
@@ -1009,7 +1171,65 @@
return __clk_alpha_pll_update_latch(pll);
}
+static int alpha_pll_fabia_prepare(struct clk_hw *hw)
+{
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+ const struct pll_vco *vco;
+ struct clk_hw *parent_hw;
+ unsigned long cal_freq, rrate;
+ u32 cal_l, val, alpha_width = pll_alpha_width(pll);
+ const char *name = clk_hw_get_name(hw);
+ u64 a;
+ int ret;
+
+ /* Check if calibration needs to be done i.e. PLL is in reset */
+ ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val);
+ if (ret)
+ return ret;
+
+ /* Return early if calibration is not needed. */
+ if (val & PLL_RESET_N)
+ return 0;
+
+ vco = alpha_pll_find_vco(pll, clk_hw_get_rate(hw));
+ if (!vco) {
+ pr_err("%s: alpha pll not in a valid vco range\n", name);
+ return -EINVAL;
+ }
+
+ cal_freq = DIV_ROUND_CLOSEST((pll->vco_table[0].min_freq +
+ pll->vco_table[0].max_freq) * 54, 100);
+
+ parent_hw = clk_hw_get_parent(hw);
+ if (!parent_hw)
+ return -EINVAL;
+
+ rrate = alpha_pll_round_rate(cal_freq, clk_hw_get_rate(parent_hw),
+ &cal_l, &a, alpha_width);
+ /*
+ * Due to a limited number of bits for fractional rate programming, the
+ * rounded up rate could be marginally higher than the requested rate.
+ */
+ if (rrate > (cal_freq + PLL_RATE_MARGIN) || rrate < cal_freq)
+ return -EINVAL;
+
+ /* Setup PLL for calibration frequency */
+ regmap_write(pll->clkr.regmap, PLL_CAL_L_VAL(pll), cal_l);
+
+ /* Bringup the PLL at calibration frequency */
+ ret = clk_alpha_pll_enable(hw);
+ if (ret) {
+ pr_err("%s: alpha pll calibration failed\n", name);
+ return ret;
+ }
+
+ clk_alpha_pll_disable(hw);
+
+ return 0;
+}
+
const struct clk_ops clk_alpha_pll_fabia_ops = {
+ .prepare = alpha_pll_fabia_prepare,
.enable = alpha_pll_fabia_enable,
.disable = alpha_pll_fabia_disable,
.is_enabled = clk_alpha_pll_is_enabled,
@@ -1035,11 +1255,6 @@
u32 i, div = 1, val;
int ret;
- if (!pll->post_div_table) {
- pr_err("Missing the post_div_table for the PLL\n");
- return -EINVAL;
- }
-
ret = regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &val);
if (ret)
return ret;
@@ -1057,15 +1272,70 @@
return (parent_rate / div);
}
+static unsigned long
+clk_trion_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
+ struct regmap *regmap = pll->clkr.regmap;
+ u32 i, div = 1, val;
+
+ regmap_read(regmap, PLL_USER_CTL(pll), &val);
+
+ val >>= pll->post_div_shift;
+ val &= PLL_POST_DIV_MASK(pll);
+
+ for (i = 0; i < pll->num_post_div; i++) {
+ if (pll->post_div_table[i].val == val) {
+ div = pll->post_div_table[i].div;
+ break;
+ }
+ }
+
+ return (parent_rate / div);
+}
+
+static long
+clk_trion_pll_postdiv_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
+
+ return divider_round_rate(hw, rate, prate, pll->post_div_table,
+ pll->width, CLK_DIVIDER_ROUND_CLOSEST);
+};
+
+static int
+clk_trion_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
+ struct regmap *regmap = pll->clkr.regmap;
+ int i, val = 0, div;
+
+ div = DIV_ROUND_UP_ULL(parent_rate, rate);
+ for (i = 0; i < pll->num_post_div; i++) {
+ if (pll->post_div_table[i].div == div) {
+ val = pll->post_div_table[i].val;
+ break;
+ }
+ }
+
+ return regmap_update_bits(regmap, PLL_USER_CTL(pll),
+ PLL_POST_DIV_MASK(pll) << PLL_POST_DIV_SHIFT,
+ val << PLL_POST_DIV_SHIFT);
+}
+
+const struct clk_ops clk_alpha_pll_postdiv_trion_ops = {
+ .recalc_rate = clk_trion_pll_postdiv_recalc_rate,
+ .round_rate = clk_trion_pll_postdiv_round_rate,
+ .set_rate = clk_trion_pll_postdiv_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_trion_ops);
+
static long clk_alpha_pll_postdiv_fabia_round_rate(struct clk_hw *hw,
unsigned long rate, unsigned long *prate)
{
struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
-
- if (!pll->post_div_table) {
- pr_err("Missing the post_div_table for the PLL\n");
- return -EINVAL;
- }
return divider_round_rate(hw, rate, prate, pll->post_div_table,
pll->width, CLK_DIVIDER_ROUND_CLOSEST);
@@ -1088,12 +1358,7 @@
if (val & PLL_VOTE_FSM_ENA)
return 0;
- if (!pll->post_div_table) {
- pr_err("Missing the post_div_table for the PLL\n");
- return -EINVAL;
- }
-
- div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
+ div = DIV_ROUND_UP_ULL(parent_rate, rate);
for (i = 0; i < pll->num_post_div; i++) {
if (pll->post_div_table[i].div == div) {
val = pll->post_div_table[i].val;
@@ -1112,3 +1377,187 @@
.set_rate = clk_alpha_pll_postdiv_fabia_set_rate,
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_fabia_ops);
+
+/**
+ * clk_trion_pll_configure - configure the trion pll
+ *
+ * @pll: clk alpha pll
+ * @regmap: register map
+ * @config: configuration to apply for pll
+ */
+void clk_trion_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
+ const struct alpha_pll_config *config)
+{
+ if (config->l)
+ regmap_write(regmap, PLL_L_VAL(pll), config->l);
+
+ regmap_write(regmap, PLL_CAL_L_VAL(pll), TRION_PLL_CAL_VAL);
+
+ if (config->alpha)
+ regmap_write(regmap, PLL_ALPHA_VAL(pll), config->alpha);
+
+ if (config->config_ctl_val)
+ regmap_write(regmap, PLL_CONFIG_CTL(pll),
+ config->config_ctl_val);
+
+ if (config->config_ctl_hi_val)
+ regmap_write(regmap, PLL_CONFIG_CTL_U(pll),
+ config->config_ctl_hi_val);
+
+ if (config->config_ctl_hi1_val)
+ regmap_write(regmap, PLL_CONFIG_CTL_U1(pll),
+ config->config_ctl_hi1_val);
+
+ if (config->user_ctl_val)
+ regmap_write(regmap, PLL_USER_CTL(pll),
+ config->user_ctl_val);
+
+ if (config->user_ctl_hi_val)
+ regmap_write(regmap, PLL_USER_CTL_U(pll),
+ config->user_ctl_hi_val);
+
+ if (config->user_ctl_hi1_val)
+ regmap_write(regmap, PLL_USER_CTL_U1(pll),
+ config->user_ctl_hi1_val);
+
+ if (config->test_ctl_val)
+ regmap_write(regmap, PLL_TEST_CTL(pll),
+ config->test_ctl_val);
+
+ if (config->test_ctl_hi_val)
+ regmap_write(regmap, PLL_TEST_CTL_U(pll),
+ config->test_ctl_hi_val);
+
+ if (config->test_ctl_hi1_val)
+ regmap_write(regmap, PLL_TEST_CTL_U1(pll),
+ config->test_ctl_hi1_val);
+
+ regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS,
+ PLL_UPDATE_BYPASS);
+
+ /* Disable PLL output */
+ regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0);
+
+ /* Set operation mode to OFF */
+ regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY);
+
+ /* Place the PLL in STANDBY mode */
+ regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N);
+}
+EXPORT_SYMBOL_GPL(clk_trion_pll_configure);
+
+/*
+ * The TRION PLL requires a power-on self-calibration which happens when the
+ * PLL comes out of reset. Calibrate in case it is not completed.
+ */
+static int __alpha_pll_trion_prepare(struct clk_hw *hw, u32 pcal_done)
+{
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+ u32 regval;
+ int ret;
+
+ /* Return early if calibration is not needed. */
+ regmap_read(pll->clkr.regmap, PLL_STATUS(pll), ®val);
+ if (regval & pcal_done)
+ return 0;
+
+ /* On/off to calibrate */
+ ret = clk_trion_pll_enable(hw);
+ if (!ret)
+ clk_trion_pll_disable(hw);
+
+ return ret;
+}
+
+static int alpha_pll_trion_prepare(struct clk_hw *hw)
+{
+ return __alpha_pll_trion_prepare(hw, TRION_PCAL_DONE);
+}
+
+static int alpha_pll_lucid_prepare(struct clk_hw *hw)
+{
+ return __alpha_pll_trion_prepare(hw, LUCID_PCAL_DONE);
+}
+
+static int alpha_pll_trion_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+ unsigned long rrate;
+ u32 regval, l, alpha_width = pll_alpha_width(pll);
+ u64 a;
+ int ret;
+
+ rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
+
+ /*
+ * Due to a limited number of bits for fractional rate programming, the
+ * rounded up rate could be marginally higher than the requested rate.
+ */
+ if (rrate > (rate + PLL_RATE_MARGIN) || rrate < rate) {
+ pr_err("Call set rate on the PLL with rounded rates!\n");
+ return -EINVAL;
+ }
+
+ regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
+ regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a);
+
+ /* Latch the PLL input */
+ ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll),
+ PLL_UPDATE, PLL_UPDATE);
+ if (ret)
+ return ret;
+
+ /* Wait for 2 reference cycles before checking the ACK bit. */
+ udelay(1);
+ regmap_read(pll->clkr.regmap, PLL_MODE(pll), ®val);
+ if (!(regval & ALPHA_PLL_ACK_LATCH)) {
+ pr_err("Lucid PLL latch failed. Output may be unstable!\n");
+ return -EINVAL;
+ }
+
+ /* Return the latch input to 0 */
+ ret = regmap_update_bits(pll->clkr.regmap, PLL_MODE(pll),
+ PLL_UPDATE, 0);
+ if (ret)
+ return ret;
+
+ if (clk_hw_is_enabled(hw)) {
+ ret = wait_for_pll_enable_lock(pll);
+ if (ret)
+ return ret;
+ }
+
+ /* Wait for PLL output to stabilize */
+ udelay(100);
+ return 0;
+}
+
+const struct clk_ops clk_alpha_pll_trion_ops = {
+ .prepare = alpha_pll_trion_prepare,
+ .enable = clk_trion_pll_enable,
+ .disable = clk_trion_pll_disable,
+ .is_enabled = clk_trion_pll_is_enabled,
+ .recalc_rate = clk_trion_pll_recalc_rate,
+ .round_rate = clk_alpha_pll_round_rate,
+ .set_rate = alpha_pll_trion_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_trion_ops);
+
+const struct clk_ops clk_alpha_pll_lucid_ops = {
+ .prepare = alpha_pll_lucid_prepare,
+ .enable = clk_trion_pll_enable,
+ .disable = clk_trion_pll_disable,
+ .is_enabled = clk_trion_pll_is_enabled,
+ .recalc_rate = clk_trion_pll_recalc_rate,
+ .round_rate = clk_alpha_pll_round_rate,
+ .set_rate = alpha_pll_trion_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_lucid_ops);
+
+const struct clk_ops clk_alpha_pll_postdiv_lucid_ops = {
+ .recalc_rate = clk_alpha_pll_postdiv_fabia_recalc_rate,
+ .round_rate = clk_alpha_pll_postdiv_fabia_round_rate,
+ .set_rate = clk_alpha_pll_postdiv_fabia_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_lucid_ops);
--
Gitblit v1.6.2