@@ -31,7 +31,10 @@
# define PLL_VOTE_FSM_ENA BIT(20)
# define PLL_FSM_ENA BIT(20)
# define PLL_VOTE_FSM_RESET BIT(21)
+# define PLL_UPDATE BIT(22)
+# define PLL_UPDATE_BYPASS BIT(23)
# define PLL_OFFLINE_ACK BIT(28)
+# define ALPHA_PLL_ACK_LATCH BIT(29)
# define PLL_ACTIVE_FLAG BIT(30)
# define PLL_LOCK_DET BIT(31)
@@ -123,6 +126,15 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
#define wait_for_pll_offline(pll) \
wait_for_pll(pll, PLL_OFFLINE_ACK, 0, "offline")
+#define wait_for_pll_update(pll) \
+ wait_for_pll(pll, PLL_UPDATE, 1, "update")
+
+#define wait_for_pll_update_ack_set(pll) \
+ wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 0, "update_ack_set")
+
+#define wait_for_pll_update_ack_clear(pll) \
+ wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 1, "update_ack_clear")
+
void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
const struct alpha_pll_config *config)
{
@@ -396,6 +408,56 @@ static void clk_alpha_pll_disable(struct clk_hw *hw)
return alpha_pll_calc_rate(prate, l, a, alpha_width);
}
+static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll)
+{
+ u32 mode;
+ int ret;
+
+ regmap_read(pll->clkr.regmap, pll_mode(pll), &mode);
+
+ /* Latch the input to the PLL */
+ regmap_update_bits(pll->clkr.regmap, pll_mode(pll), PLL_UPDATE,
+ PLL_UPDATE);
+
+ /* Make sure PLL_UPDATE request goes through*/
+ mb();
+
+ /* Wait for 2 reference cycle before checking ACK bit */
+ udelay(1);
+
+ /*
+ * PLL will latch the new L, Alpha and freq control word.
+ * PLL will respond by raising PLL_ACK_LATCH output when new programming
+ * has been latched in and PLL is being updated. When
+ * UPDATE_LOGIC_BYPASS bit is not set, PLL_UPDATE will be cleared
+ * automatically by hardware when PLL_ACK_LATCH is asserted by PLL.
+ */
+ if (mode & PLL_UPDATE_BYPASS) {
+ ret = wait_for_pll_update_ack_set(pll);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(pll->clkr.regmap, pll_mode(pll),
+ PLL_UPDATE, 0);
+
+ /* Make sure PLL_UPDATE request goes through*/
+ mb();
+ } else {
+ ret = wait_for_pll_update(pll);
+ if (ret)
+ return ret;
+ }
+
+ ret = wait_for_pll_update_ack_clear(pll);
+ if (ret)
+ return ret;
+
+ /* Wait for PLL output to stabilize */
+ udelay(10);
+
+ return 0;
+}
+
static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long prate)
{
@@ -428,7 +490,10 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll), PLL_ALPHA_EN,
PLL_ALPHA_EN);
- return 0;
+ if (!clk_hw_is_enabled(hw) || !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
+ return 0;
+
+ return clk_alpha_pll_update_latch(pll);
}
static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -54,6 +54,7 @@ struct clk_alpha_pll {
#define SUPPORTS_16BIT_ALPHA BIT(1)
#define SUPPORTS_FSM_MODE BIT(2)
#define SUPPORTS_64BIT_CONFIG_CTL BIT(3)
+#define SUPPORTS_DYNAMIC_UPDATE BIT(4)
u8 flags;
struct clk_regmap clkr;