|
@@ -1,5 +1,5 @@
|
|
/*
|
|
/*
|
|
- * Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
|
|
|
|
|
+ * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
|
|
*
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
@@ -58,6 +58,8 @@
|
|
#define PLL_TEST_CTL(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL])
|
|
#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_U(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL_U])
|
|
#define PLL_STATUS(p) ((p)->offset + (p)->regs[PLL_OFF_STATUS])
|
|
#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])
|
|
|
|
|
|
const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = {
|
|
const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = {
|
|
[CLK_ALPHA_PLL_TYPE_DEFAULT] = {
|
|
[CLK_ALPHA_PLL_TYPE_DEFAULT] = {
|
|
@@ -90,6 +92,18 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = {
|
|
[PLL_OFF_TEST_CTL] = 0x1c,
|
|
[PLL_OFF_TEST_CTL] = 0x1c,
|
|
[PLL_OFF_STATUS] = 0x24,
|
|
[PLL_OFF_STATUS] = 0x24,
|
|
},
|
|
},
|
|
|
|
+ [CLK_ALPHA_PLL_TYPE_FABIA] = {
|
|
|
|
+ [PLL_OFF_L_VAL] = 0x04,
|
|
|
|
+ [PLL_OFF_USER_CTL] = 0x0c,
|
|
|
|
+ [PLL_OFF_USER_CTL_U] = 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,
|
|
|
|
+ [PLL_OFF_OPMODE] = 0x2c,
|
|
|
|
+ [PLL_OFF_FRAC] = 0x38,
|
|
|
|
+ },
|
|
};
|
|
};
|
|
EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);
|
|
EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);
|
|
|
|
|
|
@@ -108,6 +122,12 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs);
|
|
#define PLL_HUAYRA_N_MASK 0xff
|
|
#define PLL_HUAYRA_N_MASK 0xff
|
|
#define PLL_HUAYRA_ALPHA_WIDTH 16
|
|
#define PLL_HUAYRA_ALPHA_WIDTH 16
|
|
|
|
|
|
|
|
+#define FABIA_OPMODE_STANDBY 0x0
|
|
|
|
+#define FABIA_OPMODE_RUN 0x1
|
|
|
|
+
|
|
|
|
+#define FABIA_PLL_OUT_MASK 0x7
|
|
|
|
+#define FABIA_PLL_RATE_MARGIN 500
|
|
|
|
+
|
|
#define pll_alpha_width(p) \
|
|
#define pll_alpha_width(p) \
|
|
((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \
|
|
((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \
|
|
ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH)
|
|
ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH)
|
|
@@ -441,16 +461,12 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|
return alpha_pll_calc_rate(prate, l, a, alpha_width);
|
|
return alpha_pll_calc_rate(prate, l, a, alpha_width);
|
|
}
|
|
}
|
|
|
|
|
|
-static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll,
|
|
|
|
- int (*is_enabled)(struct clk_hw *))
|
|
|
|
|
|
+
|
|
|
|
+static int __clk_alpha_pll_update_latch(struct clk_alpha_pll *pll)
|
|
{
|
|
{
|
|
int ret;
|
|
int ret;
|
|
u32 mode;
|
|
u32 mode;
|
|
|
|
|
|
- if (!is_enabled(&pll->clkr.hw) ||
|
|
|
|
- !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
regmap_read(pll->clkr.regmap, PLL_MODE(pll), &mode);
|
|
regmap_read(pll->clkr.regmap, PLL_MODE(pll), &mode);
|
|
|
|
|
|
/* Latch the input to the PLL */
|
|
/* Latch the input to the PLL */
|
|
@@ -489,6 +505,16 @@ static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll,
|
|
|
|
+ int (*is_enabled)(struct clk_hw *))
|
|
|
|
+{
|
|
|
|
+ if (!is_enabled(&pll->clkr.hw) ||
|
|
|
|
+ !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ return __clk_alpha_pll_update_latch(pll);
|
|
|
|
+}
|
|
|
|
+
|
|
static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long prate,
|
|
unsigned long prate,
|
|
int (*is_enabled)(struct clk_hw *))
|
|
int (*is_enabled)(struct clk_hw *))
|
|
@@ -832,3 +858,264 @@ const struct clk_ops clk_alpha_pll_postdiv_ro_ops = {
|
|
.recalc_rate = clk_alpha_pll_postdiv_recalc_rate,
|
|
.recalc_rate = clk_alpha_pll_postdiv_recalc_rate,
|
|
};
|
|
};
|
|
EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_ro_ops);
|
|
EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_ro_ops);
|
|
|
|
+
|
|
|
|
+void clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
|
|
|
|
+ const struct alpha_pll_config *config)
|
|
|
|
+{
|
|
|
|
+ u32 val, mask;
|
|
|
|
+
|
|
|
|
+ if (config->l)
|
|
|
|
+ regmap_write(regmap, PLL_L_VAL(pll), config->l);
|
|
|
|
+
|
|
|
|
+ if (config->alpha)
|
|
|
|
+ regmap_write(regmap, PLL_FRAC(pll), config->alpha);
|
|
|
|
+
|
|
|
|
+ if (config->config_ctl_val)
|
|
|
|
+ regmap_write(regmap, PLL_CONFIG_CTL(pll),
|
|
|
|
+ config->config_ctl_val);
|
|
|
|
+
|
|
|
|
+ if (config->post_div_mask) {
|
|
|
|
+ mask = config->post_div_mask;
|
|
|
|
+ val = config->post_div_val;
|
|
|
|
+ regmap_update_bits(regmap, PLL_USER_CTL(pll), mask, val);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS,
|
|
|
|
+ PLL_UPDATE_BYPASS);
|
|
|
|
+
|
|
|
|
+ regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int alpha_pll_fabia_enable(struct clk_hw *hw)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
|
|
|
|
+ u32 val, opmode_val;
|
|
|
|
+ struct regmap *regmap = pll->clkr.regmap;
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = regmap_read(regmap, PLL_OPMODE(pll), &opmode_val);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ /* Skip If PLL is already running */
|
|
|
|
+ if ((opmode_val & FABIA_OPMODE_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);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N,
|
|
|
|
+ PLL_RESET_N);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ ret = regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_RUN);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ ret = wait_for_pll_enable_lock(pll);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ ret = regmap_update_bits(regmap, PLL_USER_CTL(pll),
|
|
|
|
+ FABIA_PLL_OUT_MASK, FABIA_PLL_OUT_MASK);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ return regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL,
|
|
|
|
+ PLL_OUTCTRL);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void alpha_pll_fabia_disable(struct clk_hw *hw)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
|
|
|
|
+ u32 val;
|
|
|
|
+ struct regmap *regmap = pll->clkr.regmap;
|
|
|
|
+
|
|
|
|
+ ret = regmap_read(regmap, PLL_MODE(pll), &val);
|
|
|
|
+ if (ret)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /* If in FSM mode, just unvote it */
|
|
|
|
+ if (val & PLL_FSM_ENA) {
|
|
|
|
+ clk_disable_regmap(hw);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0);
|
|
|
|
+ if (ret)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /* Disable main outputs */
|
|
|
|
+ ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), FABIA_PLL_OUT_MASK,
|
|
|
|
+ 0);
|
|
|
|
+ if (ret)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /* Place the PLL in STANDBY */
|
|
|
|
+ regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_STANDBY);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static unsigned long alpha_pll_fabia_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_FRAC(pll), &frac);
|
|
|
|
+
|
|
|
|
+ return alpha_pll_calc_rate(parent_rate, l, frac, alpha_width);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int alpha_pll_fabia_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
|
|
+ unsigned long prate)
|
|
|
|
+{
|
|
|
|
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
|
|
|
|
+ u32 val, 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;
|
|
|
|
+
|
|
|
|
+ rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 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");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l);
|
|
|
|
+ regmap_write(pll->clkr.regmap, PLL_FRAC(pll), a);
|
|
|
|
+
|
|
|
|
+ return __clk_alpha_pll_update_latch(pll);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const struct clk_ops clk_alpha_pll_fabia_ops = {
|
|
|
|
+ .enable = alpha_pll_fabia_enable,
|
|
|
|
+ .disable = alpha_pll_fabia_disable,
|
|
|
|
+ .is_enabled = clk_alpha_pll_is_enabled,
|
|
|
|
+ .set_rate = alpha_pll_fabia_set_rate,
|
|
|
|
+ .recalc_rate = alpha_pll_fabia_recalc_rate,
|
|
|
|
+ .round_rate = clk_alpha_pll_round_rate,
|
|
|
|
+};
|
|
|
|
+EXPORT_SYMBOL_GPL(clk_alpha_pll_fabia_ops);
|
|
|
|
+
|
|
|
|
+const struct clk_ops clk_alpha_pll_fixed_fabia_ops = {
|
|
|
|
+ .enable = alpha_pll_fabia_enable,
|
|
|
|
+ .disable = alpha_pll_fabia_disable,
|
|
|
|
+ .is_enabled = clk_alpha_pll_is_enabled,
|
|
|
|
+ .recalc_rate = alpha_pll_fabia_recalc_rate,
|
|
|
|
+ .round_rate = clk_alpha_pll_round_rate,
|
|
|
|
+};
|
|
|
|
+EXPORT_SYMBOL_GPL(clk_alpha_pll_fixed_fabia_ops);
|
|
|
|
+
|
|
|
|
+static unsigned long clk_alpha_pll_postdiv_fabia_recalc_rate(struct clk_hw *hw,
|
|
|
|
+ unsigned long parent_rate)
|
|
|
|
+{
|
|
|
|
+ struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ val >>= pll->post_div_shift;
|
|
|
|
+ val &= BIT(pll->width) - 1;
|
|
|
|
+
|
|
|
|
+ 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_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);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int clk_alpha_pll_postdiv_fabia_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);
|
|
|
|
+ int i, val = 0, div, ret;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If the PLL is in FSM mode, then treat set_rate callback as a
|
|
|
|
+ * no-operation.
|
|
|
|
+ */
|
|
|
|
+ ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val);
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ 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(pll->clkr.regmap, PLL_USER_CTL(pll),
|
|
|
|
+ (BIT(pll->width) - 1) << pll->post_div_shift,
|
|
|
|
+ val << pll->post_div_shift);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const struct clk_ops clk_alpha_pll_postdiv_fabia_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_fabia_ops);
|