|
@@ -47,6 +47,7 @@
|
|
|
# define PLL_POST_DIV_SHIFT 8
|
|
|
# define PLL_POST_DIV_MASK 0xf
|
|
|
# define PLL_ALPHA_EN BIT(24)
|
|
|
+# define PLL_ALPHA_MODE BIT(25)
|
|
|
# define PLL_VCO_SHIFT 20
|
|
|
# define PLL_VCO_MASK 0x3
|
|
|
|
|
@@ -70,6 +71,16 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = {
|
|
|
[PLL_OFF_TEST_CTL_U] = 0x20,
|
|
|
[PLL_OFF_STATUS] = 0x24,
|
|
|
},
|
|
|
+ [CLK_ALPHA_PLL_TYPE_HUAYRA] = {
|
|
|
+ [PLL_OFF_L_VAL] = 0x04,
|
|
|
+ [PLL_OFF_ALPHA_VAL] = 0x08,
|
|
|
+ [PLL_OFF_USER_CTL] = 0x10,
|
|
|
+ [PLL_OFF_CONFIG_CTL] = 0x14,
|
|
|
+ [PLL_OFF_CONFIG_CTL_U] = 0x18,
|
|
|
+ [PLL_OFF_TEST_CTL] = 0x1c,
|
|
|
+ [PLL_OFF_TEST_CTL_U] = 0x20,
|
|
|
+ [PLL_OFF_STATUS] = 0x24,
|
|
|
+ },
|
|
|
};
|
|
|
EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);
|
|
|
|
|
@@ -81,6 +92,13 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);
|
|
|
#define ALPHA_BITWIDTH 32U
|
|
|
#define ALPHA_SHIFT(w) min(w, ALPHA_BITWIDTH)
|
|
|
|
|
|
+#define PLL_HUAYRA_M_WIDTH 8
|
|
|
+#define PLL_HUAYRA_M_SHIFT 8
|
|
|
+#define PLL_HUAYRA_M_MASK 0xff
|
|
|
+#define PLL_HUAYRA_N_SHIFT 0
|
|
|
+#define PLL_HUAYRA_N_MASK 0xff
|
|
|
+#define PLL_HUAYRA_ALPHA_WIDTH 16
|
|
|
+
|
|
|
#define pll_alpha_width(p) \
|
|
|
((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \
|
|
|
ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH)
|
|
@@ -473,7 +491,7 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
|
|
|
rate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
|
|
|
vco = alpha_pll_find_vco(pll, rate);
|
|
|
- if (!vco) {
|
|
|
+ if (pll->vco_table && !vco) {
|
|
|
pr_err("alpha pll not in a valid vco range\n");
|
|
|
return -EINVAL;
|
|
|
}
|
|
@@ -488,9 +506,11 @@ static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
|
|
|
regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a);
|
|
|
|
|
|
- regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
|
|
|
- PLL_VCO_MASK << PLL_VCO_SHIFT,
|
|
|
- vco->val << PLL_VCO_SHIFT);
|
|
|
+ if (vco) {
|
|
|
+ regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
|
|
|
+ PLL_VCO_MASK << PLL_VCO_SHIFT,
|
|
|
+ vco->val << PLL_VCO_SHIFT);
|
|
|
+ }
|
|
|
|
|
|
regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
|
|
|
PLL_ALPHA_EN, PLL_ALPHA_EN);
|
|
@@ -521,7 +541,7 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
unsigned long min_freq, max_freq;
|
|
|
|
|
|
rate = alpha_pll_round_rate(rate, *prate, &l, &a, alpha_width);
|
|
|
- if (alpha_pll_find_vco(pll, rate))
|
|
|
+ if (!pll->vco_table || alpha_pll_find_vco(pll, rate))
|
|
|
return rate;
|
|
|
|
|
|
min_freq = pll->vco_table[0].min_freq;
|
|
@@ -530,6 +550,158 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
return clamp(rate, min_freq, max_freq);
|
|
|
}
|
|
|
|
|
|
+static unsigned long
|
|
|
+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
|
|
|
+ * of [-0.5, 0.5).
|
|
|
+ */
|
|
|
+ if (a >= BIT(PLL_HUAYRA_ALPHA_WIDTH - 1))
|
|
|
+ l -= 1;
|
|
|
+
|
|
|
+ return (prate * l) + (prate * a >> PLL_HUAYRA_ALPHA_WIDTH);
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned long
|
|
|
+alpha_huayra_pll_round_rate(unsigned long rate, unsigned long prate,
|
|
|
+ u32 *l, u32 *a)
|
|
|
+{
|
|
|
+ u64 remainder;
|
|
|
+ u64 quotient;
|
|
|
+
|
|
|
+ quotient = rate;
|
|
|
+ remainder = do_div(quotient, prate);
|
|
|
+ *l = quotient;
|
|
|
+
|
|
|
+ if (!remainder) {
|
|
|
+ *a = 0;
|
|
|
+ return rate;
|
|
|
+ }
|
|
|
+
|
|
|
+ quotient = remainder << PLL_HUAYRA_ALPHA_WIDTH;
|
|
|
+ remainder = do_div(quotient, prate);
|
|
|
+
|
|
|
+ if (remainder)
|
|
|
+ quotient++;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * alpha_val should be in two’s compliment 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.
|
|
|
+ */
|
|
|
+ if (quotient >= BIT(PLL_HUAYRA_ALPHA_WIDTH - 1))
|
|
|
+ *l += 1;
|
|
|
+
|
|
|
+ *a = quotient;
|
|
|
+ return alpha_huayra_pll_calc_rate(prate, *l, *a);
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned long
|
|
|
+alpha_pll_huayra_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|
|
+{
|
|
|
+ u64 rate = parent_rate, tmp;
|
|
|
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
|
|
|
+ u32 l, alpha = 0, ctl, alpha_m, alpha_n;
|
|
|
+
|
|
|
+ regmap_read(pll->clkr.regmap, PLL_L_VAL(pll), &l);
|
|
|
+ regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &ctl);
|
|
|
+
|
|
|
+ if (ctl & PLL_ALPHA_EN) {
|
|
|
+ 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,
|
|
|
+ * pll_alpha_val<15:8>=M and pll_apla_val<7:0>=N
|
|
|
+ *
|
|
|
+ * Fout=FIN*(L+(M/N))
|
|
|
+ *
|
|
|
+ * 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
|
|
|
+ * range [-0.5, 0.5).
|
|
|
+ *
|
|
|
+ * Fout=FIN*(L+(alpha_val)/2^16)
|
|
|
+ *
|
|
|
+ * where alpha_val is two’s compliment number.
|
|
|
+ */
|
|
|
+ if (!(ctl & PLL_ALPHA_MODE))
|
|
|
+ return alpha_huayra_pll_calc_rate(rate, l, alpha);
|
|
|
+
|
|
|
+ alpha_m = alpha >> PLL_HUAYRA_M_SHIFT & PLL_HUAYRA_M_MASK;
|
|
|
+ alpha_n = alpha >> PLL_HUAYRA_N_SHIFT & PLL_HUAYRA_N_MASK;
|
|
|
+
|
|
|
+ rate *= l;
|
|
|
+ tmp = parent_rate;
|
|
|
+ if (alpha_m >= BIT(PLL_HUAYRA_M_WIDTH - 1)) {
|
|
|
+ alpha_m = BIT(PLL_HUAYRA_M_WIDTH) - alpha_m;
|
|
|
+ tmp *= alpha_m;
|
|
|
+ do_div(tmp, alpha_n);
|
|
|
+ rate -= tmp;
|
|
|
+ } else {
|
|
|
+ tmp *= alpha_m;
|
|
|
+ do_div(tmp, alpha_n);
|
|
|
+ rate += tmp;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rate;
|
|
|
+ }
|
|
|
+
|
|
|
+ return alpha_huayra_pll_calc_rate(rate, l, alpha);
|
|
|
+}
|
|
|
+
|
|
|
+static int alpha_pll_huayra_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
+ unsigned long prate)
|
|
|
+{
|
|
|
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
|
|
|
+ u32 l, a, ctl, cur_alpha = 0;
|
|
|
+
|
|
|
+ rate = alpha_huayra_pll_round_rate(rate, prate, &l, &a);
|
|
|
+
|
|
|
+ regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &ctl);
|
|
|
+
|
|
|
+ if (ctl & PLL_ALPHA_EN)
|
|
|
+ regmap_read(pll->clkr.regmap, PLL_ALPHA_VAL(pll), &cur_alpha);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Huayra PLL supports PLL dynamic programming. User can change L_VAL,
|
|
|
+ * without having to go through the power on sequence.
|
|
|
+ */
|
|
|
+ if (clk_alpha_pll_is_enabled(hw)) {
|
|
|
+ if (cur_alpha != a) {
|
|
|
+ pr_err("clock needs to be gated %s\n",
|
|
|
+ clk_hw_get_name(hw));
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
+ regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
|
|
|
+ /* Ensure that the write above goes to detect L val change. */
|
|
|
+ mb();
|
|
|
+ return wait_for_pll_enable_lock(pll);
|
|
|
+ }
|
|
|
+
|
|
|
+ regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
|
|
|
+ regmap_write(pll->clkr.regmap, PLL_ALPHA_VAL(pll), a);
|
|
|
+
|
|
|
+ if (a == 0)
|
|
|
+ regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
|
|
|
+ PLL_ALPHA_EN, 0x0);
|
|
|
+ else
|
|
|
+ regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll),
|
|
|
+ PLL_ALPHA_EN | PLL_ALPHA_MODE, PLL_ALPHA_EN);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static long alpha_pll_huayra_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
+ unsigned long *prate)
|
|
|
+{
|
|
|
+ u32 l, a;
|
|
|
+
|
|
|
+ return alpha_huayra_pll_round_rate(rate, *prate, &l, &a);
|
|
|
+}
|
|
|
+
|
|
|
const struct clk_ops clk_alpha_pll_ops = {
|
|
|
.enable = clk_alpha_pll_enable,
|
|
|
.disable = clk_alpha_pll_disable,
|
|
@@ -540,6 +712,16 @@ const struct clk_ops clk_alpha_pll_ops = {
|
|
|
};
|
|
|
EXPORT_SYMBOL_GPL(clk_alpha_pll_ops);
|
|
|
|
|
|
+const struct clk_ops clk_alpha_pll_huayra_ops = {
|
|
|
+ .enable = clk_alpha_pll_enable,
|
|
|
+ .disable = clk_alpha_pll_disable,
|
|
|
+ .is_enabled = clk_alpha_pll_is_enabled,
|
|
|
+ .recalc_rate = alpha_pll_huayra_recalc_rate,
|
|
|
+ .round_rate = alpha_pll_huayra_round_rate,
|
|
|
+ .set_rate = alpha_pll_huayra_set_rate,
|
|
|
+};
|
|
|
+EXPORT_SYMBOL_GPL(clk_alpha_pll_huayra_ops);
|
|
|
+
|
|
|
const struct clk_ops clk_alpha_pll_hwfsm_ops = {
|
|
|
.enable = clk_alpha_pll_hwfsm_enable,
|
|
|
.disable = clk_alpha_pll_hwfsm_disable,
|