Loading drivers/clk/qcom/clock-alpha-pll.c +137 −14 Original line number Diff line number Diff line Loading @@ -25,12 +25,14 @@ #define MODE_REG(pll) (*pll->base + pll->offset + 0x0) #define LOCK_REG(pll) (*pll->base + pll->offset + 0x0) #define ACTIVE_REG(pll) (*pll->base + pll->offset + 0x0) #define UPDATE_REG(pll) (*pll->base + pll->offset + 0x0) #define L_REG(pll) (*pll->base + pll->offset + 0x4) #define A_REG(pll) (*pll->base + pll->offset + 0x8) #define VCO_REG(pll) (*pll->base + pll->offset + 0x10) #define ALPHA_EN_REG(pll) (*pll->base + pll->offset + 0x10) #define OUTPUT_REG(pll) (*pll->base + pll->offset + 0x10) #define VOTE_REG(pll) (*pll->base + pll->fsm_reg_offset) #define PLL_BYPASSNL 0x2 #define PLL_RESET_N 0x4 Loading @@ -42,6 +44,13 @@ #define ALPHA_REG_BITWIDTH 40 #define ALPHA_BITWIDTH 32 /* * Enable/disable registers could be shared among PLLs when FSM voting * is used. This lock protects against potential race when multiple * PLLs are being enabled/disabled together. */ static DEFINE_SPINLOCK(alpha_pll_reg_lock); static unsigned long compute_rate(struct alpha_pll_clk *pll, u32 l_val, u32 a_val) { Loading @@ -60,10 +69,59 @@ static bool is_locked(struct alpha_pll_clk *pll) return (reg & mask) == mask; } static int alpha_pll_enable(struct clk *c) static bool is_active(struct alpha_pll_clk *pll) { u32 reg = readl_relaxed(ACTIVE_REG(pll)); u32 mask = pll->masks->active_mask; return (reg & mask) == mask; } /* * Check active_flag if PLL is in FSM mode, otherwise check lock_det * bit. This function assumes PLLs are already configured to the * right mode. */ static bool update_finish(struct alpha_pll_clk *pll) { if (pll->fsm_en_mask) return is_active(pll); else return is_locked(pll); } static int wait_for_update(struct alpha_pll_clk *pll) { struct alpha_pll_clk *pll = to_alpha_pll_clk(c); int count; for (count = WAIT_MAX_LOOPS; count > 0; count--) { if (update_finish(pll)) break; udelay(1); } if (!count) { pr_err("%s didn't lock after enabling it!\n", pll->c.dbg_name); return -EINVAL; } return 0; } static int __alpha_pll_vote_enable(struct alpha_pll_clk *pll) { u32 ena; ena = readl_relaxed(VOTE_REG(pll)); ena |= pll->fsm_en_mask; writel_relaxed(ena, VOTE_REG(pll)); mb(); return wait_for_update(pll); } static int __alpha_pll_enable(struct alpha_pll_clk *pll) { int rc; u32 mode; mode = readl_relaxed(MODE_REG(pll)); Loading @@ -80,17 +138,9 @@ static int alpha_pll_enable(struct clk *c) mode |= PLL_RESET_N; writel_relaxed(mode, MODE_REG(pll)); /* Wait for pll to lock. */ for (count = WAIT_MAX_LOOPS; count > 0; count--) { if (is_locked(pll)) break; udelay(1); } if (!count) { pr_err("%s didn't lock after enabling it!\n", c->dbg_name); return -EINVAL; } rc = wait_for_update(pll); if (rc < 0) return rc; /* Enable PLL output. */ mode |= PLL_OUTCTRL; Loading @@ -101,9 +151,33 @@ static int alpha_pll_enable(struct clk *c) return 0; } static void alpha_pll_disable(struct clk *c) static int alpha_pll_enable(struct clk *c) { struct alpha_pll_clk *pll = to_alpha_pll_clk(c); unsigned long flags; int rc; spin_lock_irqsave(&alpha_pll_reg_lock, flags); if (pll->fsm_en_mask) rc = __alpha_pll_vote_enable(pll); else rc = __alpha_pll_enable(pll); spin_unlock_irqrestore(&alpha_pll_reg_lock, flags); return rc; } static void __alpha_pll_vote_disable(struct alpha_pll_clk *pll) { u32 ena; ena = readl_relaxed(VOTE_REG(pll)); ena &= ~pll->fsm_en_mask; writel_relaxed(ena, VOTE_REG(pll)); } static void __alpha_pll_disable(struct alpha_pll_clk *pll) { u32 mode; mode = readl_relaxed(MODE_REG(pll)); Loading @@ -118,6 +192,19 @@ static void alpha_pll_disable(struct clk *c) writel_relaxed(mode, MODE_REG(pll)); } static void alpha_pll_disable(struct clk *c) { struct alpha_pll_clk *pll = to_alpha_pll_clk(c); unsigned long flags; spin_lock_irqsave(&alpha_pll_reg_lock, flags); if (pll->fsm_en_mask) __alpha_pll_vote_disable(pll); else __alpha_pll_disable(pll); spin_unlock_irqrestore(&alpha_pll_reg_lock, flags); } static u32 find_vco(struct alpha_pll_clk *pll, unsigned long rate) { unsigned long i; Loading Loading @@ -269,6 +356,38 @@ static void update_vco_tbl(struct alpha_pll_clk *pll) } } /* * Program bias count to be 0x6 (corresponds to 5us), and lock count * bits to 0 (check lock_det for locking). */ static void __set_fsm_mode(void __iomem *mode_reg) { u32 regval = readl_relaxed(mode_reg); /* De-assert reset to FSM */ regval &= ~BIT(21); writel_relaxed(regval, mode_reg); /* Program bias count */ regval &= ~BM(19, 14); regval |= BVAL(19, 14, 0x6); writel_relaxed(regval, mode_reg); /* Program lock count */ regval &= ~BM(13, 8); regval |= BVAL(13, 8, 0x0); writel_relaxed(regval, mode_reg); /* Enable PLL FSM voting */ regval |= BIT(20); writel_relaxed(regval, mode_reg); } static bool is_fsm_mode(void __iomem *mode_reg) { return !!(readl_relaxed(mode_reg) & BIT(20)); } static enum handoff alpha_pll_handoff(struct clk *c) { struct alpha_pll_clk *pll = to_alpha_pll_clk(c); Loading @@ -288,7 +407,11 @@ static enum handoff alpha_pll_handoff(struct clk *c) output_en |= pll->enable_config; writel_relaxed(output_en, OUTPUT_REG(pll)); } if (pll->fsm_en_mask) __set_fsm_mode(MODE_REG(pll)); return HANDOFF_DISABLED_CLK; } else if (pll->fsm_en_mask && !is_fsm_mode(MODE_REG(pll))) { WARN(1, "%s should be in FSM mode but is not\n", c->dbg_name); } l_val = readl_relaxed(L_REG(pll)); Loading include/soc/qcom/clock-alpha-pll.h +11 −5 Original line number Diff line number Diff line Loading @@ -19,12 +19,13 @@ #include <linux/clk/msm-clk-provider.h> struct alpha_pll_masks { u32 lock_mask; u32 update_mask; u32 vco_mask; u32 lock_mask; /* lock_det bit */ u32 active_mask; /* active_flag in FSM mode */ u32 update_mask; /* update bit for dynamic update */ u32 vco_mask; /* vco_sel bits */ u32 vco_shift; u32 alpha_en_mask; u32 output_mask; u32 alpha_en_mask; /* alpha_en bit */ u32 output_mask; /* pllout_* bits */ }; struct alpha_pll_vco_tbl { Loading @@ -43,6 +44,11 @@ struct alpha_pll_clk { struct alpha_pll_masks *masks; void *const __iomem *base; const u32 offset; /* if fsm_en_mask is set, config PLL to FSM mode */ const u32 fsm_reg_offset; const u32 fsm_en_mask; u32 enable_config; /* bitmask of outputs to be enabled */ struct alpha_pll_vco_tbl *vco_tbl; Loading Loading
drivers/clk/qcom/clock-alpha-pll.c +137 −14 Original line number Diff line number Diff line Loading @@ -25,12 +25,14 @@ #define MODE_REG(pll) (*pll->base + pll->offset + 0x0) #define LOCK_REG(pll) (*pll->base + pll->offset + 0x0) #define ACTIVE_REG(pll) (*pll->base + pll->offset + 0x0) #define UPDATE_REG(pll) (*pll->base + pll->offset + 0x0) #define L_REG(pll) (*pll->base + pll->offset + 0x4) #define A_REG(pll) (*pll->base + pll->offset + 0x8) #define VCO_REG(pll) (*pll->base + pll->offset + 0x10) #define ALPHA_EN_REG(pll) (*pll->base + pll->offset + 0x10) #define OUTPUT_REG(pll) (*pll->base + pll->offset + 0x10) #define VOTE_REG(pll) (*pll->base + pll->fsm_reg_offset) #define PLL_BYPASSNL 0x2 #define PLL_RESET_N 0x4 Loading @@ -42,6 +44,13 @@ #define ALPHA_REG_BITWIDTH 40 #define ALPHA_BITWIDTH 32 /* * Enable/disable registers could be shared among PLLs when FSM voting * is used. This lock protects against potential race when multiple * PLLs are being enabled/disabled together. */ static DEFINE_SPINLOCK(alpha_pll_reg_lock); static unsigned long compute_rate(struct alpha_pll_clk *pll, u32 l_val, u32 a_val) { Loading @@ -60,10 +69,59 @@ static bool is_locked(struct alpha_pll_clk *pll) return (reg & mask) == mask; } static int alpha_pll_enable(struct clk *c) static bool is_active(struct alpha_pll_clk *pll) { u32 reg = readl_relaxed(ACTIVE_REG(pll)); u32 mask = pll->masks->active_mask; return (reg & mask) == mask; } /* * Check active_flag if PLL is in FSM mode, otherwise check lock_det * bit. This function assumes PLLs are already configured to the * right mode. */ static bool update_finish(struct alpha_pll_clk *pll) { if (pll->fsm_en_mask) return is_active(pll); else return is_locked(pll); } static int wait_for_update(struct alpha_pll_clk *pll) { struct alpha_pll_clk *pll = to_alpha_pll_clk(c); int count; for (count = WAIT_MAX_LOOPS; count > 0; count--) { if (update_finish(pll)) break; udelay(1); } if (!count) { pr_err("%s didn't lock after enabling it!\n", pll->c.dbg_name); return -EINVAL; } return 0; } static int __alpha_pll_vote_enable(struct alpha_pll_clk *pll) { u32 ena; ena = readl_relaxed(VOTE_REG(pll)); ena |= pll->fsm_en_mask; writel_relaxed(ena, VOTE_REG(pll)); mb(); return wait_for_update(pll); } static int __alpha_pll_enable(struct alpha_pll_clk *pll) { int rc; u32 mode; mode = readl_relaxed(MODE_REG(pll)); Loading @@ -80,17 +138,9 @@ static int alpha_pll_enable(struct clk *c) mode |= PLL_RESET_N; writel_relaxed(mode, MODE_REG(pll)); /* Wait for pll to lock. */ for (count = WAIT_MAX_LOOPS; count > 0; count--) { if (is_locked(pll)) break; udelay(1); } if (!count) { pr_err("%s didn't lock after enabling it!\n", c->dbg_name); return -EINVAL; } rc = wait_for_update(pll); if (rc < 0) return rc; /* Enable PLL output. */ mode |= PLL_OUTCTRL; Loading @@ -101,9 +151,33 @@ static int alpha_pll_enable(struct clk *c) return 0; } static void alpha_pll_disable(struct clk *c) static int alpha_pll_enable(struct clk *c) { struct alpha_pll_clk *pll = to_alpha_pll_clk(c); unsigned long flags; int rc; spin_lock_irqsave(&alpha_pll_reg_lock, flags); if (pll->fsm_en_mask) rc = __alpha_pll_vote_enable(pll); else rc = __alpha_pll_enable(pll); spin_unlock_irqrestore(&alpha_pll_reg_lock, flags); return rc; } static void __alpha_pll_vote_disable(struct alpha_pll_clk *pll) { u32 ena; ena = readl_relaxed(VOTE_REG(pll)); ena &= ~pll->fsm_en_mask; writel_relaxed(ena, VOTE_REG(pll)); } static void __alpha_pll_disable(struct alpha_pll_clk *pll) { u32 mode; mode = readl_relaxed(MODE_REG(pll)); Loading @@ -118,6 +192,19 @@ static void alpha_pll_disable(struct clk *c) writel_relaxed(mode, MODE_REG(pll)); } static void alpha_pll_disable(struct clk *c) { struct alpha_pll_clk *pll = to_alpha_pll_clk(c); unsigned long flags; spin_lock_irqsave(&alpha_pll_reg_lock, flags); if (pll->fsm_en_mask) __alpha_pll_vote_disable(pll); else __alpha_pll_disable(pll); spin_unlock_irqrestore(&alpha_pll_reg_lock, flags); } static u32 find_vco(struct alpha_pll_clk *pll, unsigned long rate) { unsigned long i; Loading Loading @@ -269,6 +356,38 @@ static void update_vco_tbl(struct alpha_pll_clk *pll) } } /* * Program bias count to be 0x6 (corresponds to 5us), and lock count * bits to 0 (check lock_det for locking). */ static void __set_fsm_mode(void __iomem *mode_reg) { u32 regval = readl_relaxed(mode_reg); /* De-assert reset to FSM */ regval &= ~BIT(21); writel_relaxed(regval, mode_reg); /* Program bias count */ regval &= ~BM(19, 14); regval |= BVAL(19, 14, 0x6); writel_relaxed(regval, mode_reg); /* Program lock count */ regval &= ~BM(13, 8); regval |= BVAL(13, 8, 0x0); writel_relaxed(regval, mode_reg); /* Enable PLL FSM voting */ regval |= BIT(20); writel_relaxed(regval, mode_reg); } static bool is_fsm_mode(void __iomem *mode_reg) { return !!(readl_relaxed(mode_reg) & BIT(20)); } static enum handoff alpha_pll_handoff(struct clk *c) { struct alpha_pll_clk *pll = to_alpha_pll_clk(c); Loading @@ -288,7 +407,11 @@ static enum handoff alpha_pll_handoff(struct clk *c) output_en |= pll->enable_config; writel_relaxed(output_en, OUTPUT_REG(pll)); } if (pll->fsm_en_mask) __set_fsm_mode(MODE_REG(pll)); return HANDOFF_DISABLED_CLK; } else if (pll->fsm_en_mask && !is_fsm_mode(MODE_REG(pll))) { WARN(1, "%s should be in FSM mode but is not\n", c->dbg_name); } l_val = readl_relaxed(L_REG(pll)); Loading
include/soc/qcom/clock-alpha-pll.h +11 −5 Original line number Diff line number Diff line Loading @@ -19,12 +19,13 @@ #include <linux/clk/msm-clk-provider.h> struct alpha_pll_masks { u32 lock_mask; u32 update_mask; u32 vco_mask; u32 lock_mask; /* lock_det bit */ u32 active_mask; /* active_flag in FSM mode */ u32 update_mask; /* update bit for dynamic update */ u32 vco_mask; /* vco_sel bits */ u32 vco_shift; u32 alpha_en_mask; u32 output_mask; u32 alpha_en_mask; /* alpha_en bit */ u32 output_mask; /* pllout_* bits */ }; struct alpha_pll_vco_tbl { Loading @@ -43,6 +44,11 @@ struct alpha_pll_clk { struct alpha_pll_masks *masks; void *const __iomem *base; const u32 offset; /* if fsm_en_mask is set, config PLL to FSM mode */ const u32 fsm_reg_offset; const u32 fsm_en_mask; u32 enable_config; /* bitmask of outputs to be enabled */ struct alpha_pll_vco_tbl *vco_tbl; Loading